# Bridging from Chiliz Chain to Base

To bridge an existing token from Chiliz Chain to Base, you need to write and deploy two separate smart contracts: an OFT Adapter on the chain where the token already exists (Chiliz Chain), and a Native OFT on the destination chain (Base).

{% hint style="info" %}
Note that LayerZero provide [their own QuickStart for OFT contracts](https://docs.layerzero.network/v2/developers/evm/oft/quickstart), as well as a tutorial to learn how to [mint from Chiliz Chain to another EVM chain](https://docs.layerzero.network/v2/deployments/evm-chains/chiliz-mainnet-oft-quickstart) such as Base. That tutorial uses Optimism as the target chain, but you can replace it with [Base's details](https://docs.layerzero.network/v2/deployments/chains/base).
{% endhint %}

## Prerequisites

This guide requires the following:

* An ERC20 token contract address on Chiliz Chain.
* A Web3 wallet (such as MetaMask) set up to work with both Chiliz Chain and Base.
  * [See here for the Chiliz Chain RPC configuration](/develop/basics/connect-to-chiliz-chain/connect-using-rpc.md).
  * [See here for the Base Mainnet RPC configuration](https://docs.base.org/base-chain/quickstart/connecting-to-base).
* Enough gas tokens on each chain to pay for the contract deployments as well as the gas fees for sending messages.
  * On Chiliz Chain: CHZ tokens.
  * On Base: ETH tokens.

We will use [Hardhat](https://hardhat.org/) as the dev environment, and Node/npx.

## Step 1: Contract Development

### Preparing the `OFTAdapter` on Chiliz Chain

{% hint style="warning" %}
This is a preparatory step, do not deploy right away!\
You will deploy in Step 3.
{% endhint %}

The `OFTAdapter` contract acts as a lockbox for your existing token. When users bridge their tokens away from Chiliz Chain, this contract locks the original ERC20 tokens.

Create a new file named `ChilizTokenAdapter.sol` in your contracts folder:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { OFTAdapter } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFTAdapter.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

contract ChilizTokenAdapter is OFTAdapter {
    constructor(
        address _token,      // The address of your EXISTING Token on Chiliz Chain
        address _lzEndpoint, // The LayerZero V2 Endpoint address on Chiliz
        address _delegate    // The address capable of making configuration changes (usually your wallet)
    ) OFTAdapter(_token, _lzEndpoint, _delegate) Ownable(_delegate) {}
}
```

{% hint style="info" %}
As you can see this contract extends the [LayerZero OFT Adapter contract](https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/oapp/contracts/oft/OFTAdapter.sol), as well as a standard `Ownable.sol` contract to give the administrative keys. You need both so that you, and only you, can safely wire the chains together.
{% endhint %}

When deploying this to Chiliz Chain Mainnet, you must pass your existing token's contract address as `_token` and the Chiliz Endpoint V2 address (`0x6F475642a6e85809B1c36Fa62763669b1b48DD5B`) as `_lzEndpoint`.

See all available endpoints for Chiliz Chain here:

{% embed url="<https://docs.layerzero.network/v2/deployments/chains/chiliz>" %}

### Preparing the `OFT` on Base

Since the token does not natively exist on Base yet, you must deploy a standard OFT contract. This contract has the authority to mint new tokens when it receives a valid message from the Chiliz-deployed OFT Adapter, and burn them when users bridge back.

Create a new file named `BaseTokenOFT.sol` in your contracts folder:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { OFT } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFT.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

contract BaseTokenOFT is OFT {
    constructor(
        string memory _name,   // The name of the token (Should match your Chiliz Token)
        string memory _symbol, // The symbol of the token (Should match your Chiliz Token)
        address _lzEndpoint,   // The LayerZero V2 Endpoint address on Base
        address _delegate      // The address capable of making configuration changes
    ) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {}
}
```

{% hint style="info" %}
As you can see this contract extends the [LayerZero OFT contract](https://github.com/LayerZero-Labs/LayerZero-v2/blob/main/packages/layerzero-v2/evm/oapp/contracts/oft/OFT.sol) as well as a standard `Ownable.sol` contract, for the same reasons as cited above.
{% endhint %}

When deploying this to Base, make sure that the `_name` and `_symbol` match your original token on Chiliz Chain to avoid confusing users. Pass the Base Endpoint V2 address (`0x1a44076050125825900e736c501f859c50fE728c`) as the `_lzEndpoint`.

See all available endpoints for Base here:

{% embed url="<https://docs.layerzero.network/v2/deployments/chains/base>" %}

## Step 2: LayerZero Configuration

Once your contracts are ready, you need to tell the LayerZero tooling how they connect. This is done using a `layerzero.config.ts` file located in the root of your Hardhat project.

This configuration file acts as the blueprint for your cross-chain architecture. It maps your deployed smart contracts to their respective LayerZero Endpoint IDs (EIDs) and defines the pathways (connections) between them.

The [Simple Config Generator](https://docs.layerzero.network/v2/tools/simple-config) is the recommended way to generate wiring configuration, as it automates bidirectional wiring and applies recommended security configurations under the hood.

Create or update your `layerzero.config.ts` file in the root of your Hardhat project:

```typescript
import { ExecutorOptionType } from '@layerzerolabs/lz-v2-utilities';
import { OAppEnforcedOption, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat';
import { EndpointId } from '@layerzerolabs/lz-definitions';
import { generateConnectionsConfig } from '@layerzerolabs/metadata-tools';

// Define the Contracts
const chilizContract: OmniPointHardhat = {
    eid: EndpointId.CHILIZ_V2_MAINNET,
    contractName: 'ChilizTokenAdapter',
};

const baseContract: OmniPointHardhat = {
    eid: EndpointId.BASE_V2_MAINNET,
    contractName: 'BaseFanTokenOFT',
};

// Define standard EVM Gas Limits (Enforced Options)
const EVM_ENFORCED_OPTIONS: OAppEnforcedOption[] = [
    {
        msgType: 1,  // Standard OFT Transfer
        optionType: ExecutorOptionType.LZ_RECEIVE,
        gas: 200000, // Safe baseline gas limit for minting/unlocking
        value: 0,    
    },
];

// Export the Generated Configuration
export default async function () {
    return {
        contracts: [
            { contract: chilizContract },
            { contract: baseContract },
        ],
        connections: await generateConnectionsConfig([
            [
                chilizContract,  // Source
                baseContract,    // Destination
                [['LayerZero Labs'], []], // Default DVN Configuration
                [1, 1],                   // Block confirmations
                [EVM_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Execution Options [To Chiliz, To Base]
            ],
        ]),
    };
}
```

## Step 3: Deployment Workflow

With your contracts written and your `layerzero.config.ts` prepared, it is time to deploy the contracts to their respective networks. The LayerZero V2 toolbox relies on the `hardhat-deploy` plugin to manage deployments efficiently.

You will need to create two deployment scripts in your project's `deploy/` directory: one for the Chiliz Chain Mainnet and one for Base Mainnet.

### The Chiliz Adapter Deployment Script

Create a file named `01_deploy_chiliz_adapter.ts` in your `deploy/` folder. This script passes your existing token address and the Chiliz Endpoint V2 address into the constructor.

```typescript
import { DeployFunction } from 'hardhat-deploy/types';

const deployAdapter: DeployFunction = async ({ getNamedAccounts, deployments }) => {
    const { deploy } = deployments;
    const { deployer } = await getNamedAccounts();

    // The address of your existing ERC-20 token on Chiliz
    const TOKEN_ADDRESS = "0xYourTokenAddressHere"; 
    
    // The Chiliz Mainnet Endpoint V2 Address
    const CHILIZ_ENDPOINT_V2 = "0x6F475642a6e85809B1c36Fa62763669b1b48DD5B";

    console.log("Deploying OFTAdapter to Chiliz Mainnet...");

    await deploy('ChilizTokenAdapter', {
        from: deployer,
        args: [
            TOKEN_ADDRESS,   // _token
            CHILIZ_ENDPOINT_V2,  // _lzEndpoint
            deployer             // _delegate (Owner)
        ],
        log: true,
        waitConfirmations: 2,
    });
};

export default deployAdapter;
deployAdapter.tags = ['ChilizTokenAdapter'];
```

### The Base OFT Deployment Script

Create a second file named `02_deploy_base_oft.ts` in your `deploy/` folder. This script initializes the new Native OFT on Base.

```typescript
import { DeployFunction } from 'hardhat-deploy/types';

const deployOFT: DeployFunction = async ({ getNamedAccounts, deployments }) => {
    const { deploy } = deployments;
    const { deployer } = await getNamedAccounts();

    // Token Details (Must match your Chiliz Token)
    const TOKEN_NAME = "My Token";
    const TOKEN_SYMBOL = "TKN";

    // The Base Mainnet Endpoint V2 Address 
    const BASE_ENDPOINT_V2 = "0x1a44076050125825900e736c501f859c50fE728c";

    console.log("Deploying Native OFT to Base Mainnet...");

    await deploy('BaseTokenOFT', {
        from: deployer,
        args: [
            TOKEN_NAME,         // _name
            TOKEN_SYMBOL,       // _symbol
            BASE_ENDPOINT_V2,   // _lzEndpoint
            deployer            // _delegate (Owner)
        ],
        log: true,
        waitConfirmations: 2,
    });
};

export default deployOFT;
deployOFT.tags = ['BaseTokenOFT'];
```

### Executing the Deployments

Make sure that your `hardhat.config.ts` has the RPC URLs and private keys properly configured for both Chiliz Chain and Base.

{% content-ref url="/pages/oYKKHjOmAZJp2Vfye0kX" %}
[Connect using RPC](/develop/basics/connect-to-chiliz-chain/connect-using-rpc.md)
{% endcontent-ref %}

{% embed url="<https://docs.base.org/base-chain/quickstart/connecting-to-base>" %}

Run the following commands in your terminal to deploy the contracts:

```bash
# 1. Deploy the Adapter to Chiliz Chain
npx hardhat deploy --network chiliz --tags ChilizTokenAdapter

# 2. Deploy the Native OFT to Base
npx hardhat deploy --network base --tags BaseTokenOFT
```

Once the deployments are complete, Hardhat will save the contract addresses in a `deployments/` folder. The LayerZero tooling will automatically read these addresses when we run the wiring command in the next step.

## Step 4: Wiring & Peering

At this stage, your `ChilizTokenAdapter` and `BaseTokenOFT` contracts are deployed on each chain, but they are completely isolated.\
If the Base contract receives a message instructing it to mint tokens, it needs to know that the message genuinely originated from *your* Adapter on Chiliz, rather than a malicious actor.

You must establish this trust by cryptographically "wiring" the contracts together as peers.

This is done by running the `wire` command, which will generate and execute transactions on both Chiliz and Base.

{% hint style="info" %}
**What Happens Under the Hood?**

A complete LayerZero V2 pathway configuration actually requires 6 transactions on each chain:

* `setSendLibrary`: Assigns the LayerZero MessageLib responsible for sending messages (e.g., configuring it to use the V2 Send Library).
* `setReceiveLibrary`: Assigns the MessageLib responsible for receiving messages. (Includes a `gracePeriod`, typically set to `0`).
* `setConfig` (Send Library): Sets the specific Decentralized Verifier Networks (DVNs) and Executors for outbound messages.
* `setConfig` (Receive Library): Sets the specific DVNs required to verify inbound messages.
* `setEnforcedOptions`: sets the required execution gas limits. When a user sends a message from Chiliz Chain, they pay for the destination gas upfront. Setting enforced options ensures they pay enough gas for the LayerZero Executor to successfully process the transaction on Chiliz Chain.
* `setPeer`: Links a destination Endpoint ID (EID) to a trusted contract address. You must tell the Chiliz Chain contract to trust the Base contract, and the Base contract to trust the Chiliz Chain contract.
  {% endhint %}

### Executing the Wire Task

Run the following command in your terminal:

```bash
npx hardhat lz:oapp:wire --oapp-config layerzero.config.ts
```

The toolchain will automatically calculate the required parameters, prompt you with a table of the transactions it is about to execute, and submit them to Chiliz and Base using your deployer wallet.

Once the transactions confirm on both networks, your token bridge is officially live and fully configured! The Chiliz Adapter is now authorized to send mint commands to Base, and Base is authorized to send unlock commands back to Chiliz.

## Step 5: Operation & Testing

With your contracts deployed and securely wired together, your token is now omnichain. The final step is to execute a cross-chain transfer from Chiliz Chain Mainnet to Base Mainnet.

### 1) Executing the Cross-Chain Transfer

The LayerZero V2 toolbox provides a built-in Hardhat task to test OFT transfers directly from your terminal. This command will estimate the cross-chain gas fee, charge your wallet in $CHZ (the native gas token on the source chain), and initiate the transfer.

{% hint style="info" %}
Because the `ChilizTokenAdapter` needs permission to lock your existing Fan Tokens, an ERC-20 `approve()` transaction is required.

The LayerZero CLI detects this and handles the approval automatically before sending the message.
{% endhint %}

Run the following command:

{% code overflow="wrap" lineNumbers="true" %}

```bash
npx hardhat lz:oft:send --oapp-config layerzero.config.ts --from-eid 30409 --to-eid 30184 --amount 10
```

{% endcode %}

What this command does:

1. Looks up the Chiliz Mainnet (`30409`) and Base Mainnet (`30184`) in your config.
2. Quotes the cross-chain fee required by the LayerZero Executor.
3. Calls the `send()` function on your Chiliz Adapter.
4. The Adapter locks 10 tokens and emits a packet to the LayerZero Endpoint.

### 2) Tracking the Packet on LayerZero Scan

Cross-chain transactions are asynchronous. While your Chiliz transaction will confirm in a few seconds, the message still needs to be verified and executed on Base.

To track this journey in real-time, click the LayerZero Scan link that appears in the CLI output.

Learn more about this tool here:

{% embed url="<https://docs.layerzero.network/v2/developers/evm/tooling/layerzeroscan>" %}

## Setting Up Gas Limits

It is highly important to set gas limits, as failing to do so can cause your cross-chain messages to fail on the destination network.

When a user bridges a token from Chiliz Chain to Base, they pay the gas fee for *both* chains upfront in a single transaction on Chiliz (using `$CHZ`). LayerZero’s then uses a portion of that fee to pay the actual ETH gas required to execute the minting transaction on Base.

To ensure that LayerZero has enough gas to successfully process the `lzReceive` function on the destination chain, you must set Enforced Options. If these are not set, the transaction may revert on the destination chain due to an "Out of Gas" error.

### **Configuring Options**

This option is set up in your `layerzero.config.ts` file. Update the `connections` array to include the `enforcedOptions` block for each pathway:

{% code overflow="wrap" %}

```typescript
import { EndpointId } from '@layerzerolabs/lz-definitions';

// ... (contract definitions as before)

connections: [
    {
        from: 'ChilizTokenAdapter',
        to: 'BaseTokenOFT',
        config: {
            enforcedOptions: [
                {
                    msgType: 1,        // 1 = Standard OFT Transfer (SEND)
                    optionType: 1,     // 1 = lzReceive Option
                    gas: 200000,       // Estimated gas limit to mint on Base
                },
                {
                    msgType: 2,        // 2 = OFT Transfer with payload (SEND_AND_CALL)
                    optionType: 1,
                    gas: 250000,       // Slightly higher gas limit for complex calls
                }
            ]
        },
    },
    {
        from: 'BaseTokenOFT',
        to: 'ChilizTokenAdapter',
        config: {
            enforcedOptions: [
                {
                    msgType: 1,
                    optionType: 1,
                    gas: 200000,       // Estimated gas limit to unlock on Chiliz
                }
            ]
        },
    },
],
// ...
```

{% endcode %}

Explanation of the parameters:

* `msgType: 1`: Represents a standard token transfer. `msgType: 2` is used for "composed" calls (e.g., bridging a token and immediately staking it in one click).
* `optionType: 1`: Instructs the Executor to simply execute the `lzReceive` function.
* `gas: 200000`: A safe baseline gas limit for standard OFT mints and unlocks. You can adjust this based on the specific gas consumption of your deployed contracts.

If have set this option after already wiring the chains, you can rewire with the options now in place by using the same command:

{% code overflow="wrap" %}

```bash
npx hardhat lz:oapp:wire --oapp-config layerzero.config.ts
```

{% endcode %}

The tooling will automatically detect the changes and submit a `setEnforcedOptions()` transaction to your contracts on both Chiliz and Base. Now, whenever a user triggers a bridge, the contracts will mandate that the user pays for at least 200,000 gas units on the destination chain, ensuring reliable delivery.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.chiliz.com/develop/advanced/use-omnichain-tokens/bridging-from-chiliz-chain-to-base.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
