Template project for converting an existing token into a cross-chain token (OFT) using the LayerZero protocol. This example's config involves EVM chains, but the same OFT can be extended to involve other VM chains such as Solana, Aptos and Hyperliquid.
- Prerequisite Knowledge
- Introduction
- Requirements
- Scaffold this example
- Helper Tasks
- Setup
- Build
- Deploy
- Enable Messaging
- Sending OFTs
- Next Steps
- Production Deployment Checklist
- Appendix
OFT Adapter - while a regular OFT uses the mint/burn mechanism, an OFT adapter uses lock/unlock. The OFT Adapter contract functions as a lockbox for the existing token (referred to as the inner token). Given the inner token's chain, transfers to outside the inner token's chain will require locking and transfers to the inner token's chain will result in unlocking.
Node.js
->=18.16.0
pnpm
(recommended) - or another package manager of your choice (npm, yarn)forge
(optional) ->=0.2.0
for testing, and if not using Hardhat for compilation
Create your local copy of this example:
pnpm dlx create-lz-oapp@latest --example oft-adapter
Specify the directory, select OFTAdapter
and proceed with the installation.
Note that create-lz-oapp
will also automatically run the dependencies install step for you.
Throughout this walkthrough, helper tasks will be used. For the full list of available helper tasks, refer to the LayerZero Hardhat Helper Tasks section. All commands can be run at the project root.
-
Copy
.env.example
into a new.env
-
Set up your deployer address/account via the
.env
-
You can specify either
MNEMONIC
orPRIVATE_KEY
:MNEMONIC="test test test test test test test test test test test junk" or... PRIVATE_KEY="0xabc...def"
-
-
Fund this deployer address/account with the native tokens of the chains you want to deploy to. This example by default will deploy to the following chains' testnets: Optimism and Arbitrum.
This project supports both hardhat
and forge
compilation. By default, the compile
command will execute both:
pnpm compile
If you prefer one over the other, you can use the tooling-specific commands:
pnpm compile:forge
pnpm compile:hardhat
First, deploy the inner token to (only) Optimism Sepolia.
pnpm hardhat lz:deploy --tags MyERC20Mock --networks optimism-testnet
The deploy script for MyERC20Mock will also mint 10 tokens to the deployer address.
On the Deployed Contract
line, note the address
logged (inner token's address) upon successful deployment as you need it for the next step. Else, you can also refer to ./deployments/optimism-testnet/MyERC20Mock.json
.
βΉοΈ MyERC20Mock will be used as it provides a public mint function which we require for testing. Ensure you do not use this for production.
In the hardhat.config.ts
file, add the inner token's address to the network you want to deploy the OFTAdapter to:
// Replace `0x0` with the address of the ERC20 token you want to adapt to the OFT functionality.
oftAdapter: {
tokenAddress: '<INNER_TOKEN_ADDRESS>',
}
Deploy an OFTAdapter to Optimism Sepolia:
pnpm hardhat lz:deploy --tags MyOFTAdapter --networks optimism-testnet
Deploy the OFT to Arbitrum Sepolia:
pnpm hardhat lz:deploy --tags MyOFT --networks arbitrum-testnet
The OFT standard builds on top of the OApp standard, which enables generic message-passing between chains. After deploying the OFT on the respective chains, you enable messaging by running the wiring task.
Run the wiring task:
pnpm hardhat lz:oapp:wire --oapp-config layerzero.config.ts
Submit all the transactions to complete wiring. After all transactions confirm, your OApps are wired and can send messages to each other.
With your OFTs wired, you can now send them cross chain.
Send 1 OFT from Optimism Sepolia to Arbitrum Sepolia:
pnpm hardhat lz:oft:send --src-eid 40232 --dst-eid 40231 --amount 1 --to <EVM_ADDRESS>
βΉοΈ
40232
and40106
are the Endpoint IDs of Optimism Sepolia and Arbitrum Sepolia respectively. View the list of chains and their Endpoint IDs on the Deployed Endpoints page.
Upon a successful send, the script will provide you with the link to the message on LayerZero Scan.
Once the message is delivered, you will be able to click on the destination transaction hash to verify that the OFT was sent.
Congratulations, you have now sent an OFT cross-chain!
If you run into any issues, refer to Troubleshooting.
Now that you've gone through a simplified walkthrough, here are what you can do next.
- If you are planning to deploy to production, go through the Production Deployment Checklist.
- Read on DVNs / Security Stack
- Read on Message Execution Options
Before deploying, ensure the following:
- (required) you are not using
MyOFTMock
, which has a publicmint
function- In
layerzero.config.ts
, ensure you are not usingMyOFTMock
as thecontractName
for any of the contract objects.
- In
- (recommended) you have profiled the gas usage of
lzReceive
on your destination chains
The optimal values you should specify for the gas
parameter in the LZ Config depends on the destination chain, and requires profiling. This section walks through how to estimate the optimal gas
value.
This guide explains how to use the pnpm
commands to estimate gas usage for LayerZero's lzReceive
and lzCompose
functions. These commands wrap Foundry scripts for easier invocation and allow you to pass the required arguments dynamically.
-
gas:lzReceive
This command profiles the
lzReceive
function for estimating gas usage across multiple runs."gas:lzReceive": "forge script scripts/GasProfiler.s.sol:GasProfilerScript --via-ir --sig 'run_lzReceive(string,address,uint32,address,uint32,address,bytes,uint256,uint256)'"
-
gas:lzCompose
This command profiles the
lzCompose
function for estimating gas usage across multiple runs."gas:lzCompose": "forge script scripts/GasProfiler.s.sol:GasProfilerScript --via-ir --sig 'run_lzCompose(string,address,uint32,address,uint32,address,address,bytes,uint256,uint256)'"
To estimate the gas for the lzReceive
function:
pnpm gas:lzReceive
<rpcUrl> \
<endpointAddress> \
<srcEid> \
<sender> \
<dstEid> \
<receiver> \
<message> \
<msg.value> \
<numOfRuns>
Where:
rpcUrl
: The RPC URL for the target blockchain (e.g., Optimism, Arbitrum, etc.).endpointAddress
: The deployed LayerZero EndpointV2 contract address.srcEid
: The source endpoint ID (uint32).sender
: The sender's address (OApp).dstEid
: The destination endpoint ID (uint32).receiver
: The address intended to receive the message (OApp).message
: The message payload as abytes
array.msg.value
: The amount of Ether sent with the message (in wei).numOfRuns
: The number of test runs to execute.
To estimate the gas for the lzCompose
function:
pnpm gas:lzCompose
<rpcUrl> \
<endpointAddress> \
<srcEid> \
<sender> \
<dstEid> \
<receiver> \
<composer> \
<composeMsg> \
<msg.value> \
<numOfRuns>
Where:
rpcUrl
: The RPC URL for the target blockchain (e.g., Optimism, Arbitrum, etc.).endpointAddress
: The deployed LayerZero EndpointV2 contract address.srcEid
: The source endpoint ID (uint32).sender
: The originating OApp address.dstEid
: The destination endpoint ID (uint32).receiver
: The address intended to receive the message (OApp).composer
: The LayerZero Composer contract address.composeMsg
: The compose message payload as abytes
array.msgValue
: The amount of Ether sent with the message (in wei).numOfRuns
: The number of test runs to execute.
- Modify
numOfRuns
based on the level of accuracy or performance you require for gas profiling. - Log outputs will provide metrics such as the average, median, minimum, and maximum gas usage across all successful runs.
This approach simplifies repetitive tasks and ensures consistent testing across various configurations.
Join our community! | Follow us on X (formerly Twitter)
Similar to the contract compilation, we support both hardhat
and forge
tests. By default, the test
command will execute both:
pnpm test
If you prefer one over the other, you can use the tooling-specific commands:
pnpm test:forge
pnpm test:hardhat
If you're adding another EVM chain, first, add it to the hardhat.config.ts
. Adding non-EVM chains do not require modifying the hardhat.config.ts
.
Then, modify layerzero.config.ts
with the following changes:
- declare a new contract object (specifying the
eid
andcontractName
) - decide whether to use an existing EVM enforced options variable or declare a new one
- create a new entry in the
pathways
variable - add the new contract into the
contracts
key of thereturn
of theexport default
function
After applying the desired changes, make sure you re-run the wiring task:
pnpm hardhat lz:oapp:wire --oapp-config layerzero.config.ts
The wiring task supports the usage of Safe Multisigs.
To use a Safe multisig as the signer for these transactions, add the following to each network in your hardhat.config.ts
and add the --safe
flag to lz:oapp:wire --safe
:
// hardhat.config.ts
networks: {
// Include configurations for other networks as needed
fuji: {
/* ... */
// Network-specific settings
safeConfig: {
safeUrl: 'http://something', // URL of the Safe API, not the Safe itself
safeAddress: 'address'
}
}
}
LayerZero Devtools provides several helper hardhat tasks to easily deploy, verify, configure, connect, and send OFTs cross-chain.
pnpm hardhat lz:deploy
Deploys your contract to any of the available networks in your hardhat.config.ts
when given a deploy tag (by default contract name) and returns a list of available networks to select for the deployment. For specifics around all deployment options, please refer to the Deploying Contracts section of the documentation. LayerZero's lz:deploy
utilizes hardhat-deploy
.
'arbitrum-sepolia': {
eid: EndpointId.ARBSEP_V2_TESTNET,
url: process.env.RPC_URL_ARBSEP_TESTNET,
accounts,
},
'base-sepolia': {
eid: EndpointId.BASESEP_V2_TESTNET,
url: process.env.RPC_URL_BASE_TESTNET,
accounts,
},
More information about available CLI arguments can be found using the --help
flag:
pnpm hardhat lz:deploy --help
pnpm hardhat lz:oapp:config:init --oapp-config YOUR_OAPP_CONFIG --contract-name CONTRACT_NAME
Initializes a layerzero.config.ts
file for all available pathways between your hardhat networks with the current LayerZero default placeholder settings. This task can be incredibly useful for correctly formatting your config file.
You can run this task by providing the contract-name
you want to set for the config and file-name
you want to generate:
pnpm hardhat lz:oapp:config:init --contract-name CONTRACT_NAME --oapp-config FILE_NAME
This will create a layerzero.config.ts
in your working directory populated with your contract name and connections for every pathway possible between your hardhat networks:
import { EndpointId } from "@layerzerolabs/lz-definitions";
const arbsepContract = {
eid: EndpointId.ARBSEP_V2_TESTNET,
contractName: "MyOFT",
};
const sepoliaContract = {
eid: EndpointId.SEPOLIA_V2_TESTNET,
contractName: "MyOFT",
};
export default {
contracts: [{ contract: arbsepContract }, { contract: sepoliaContract }],
connections: [
{
from: arbsepContract,
to: sepoliaContract,
config: {
sendLibrary: "0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E",
receiveLibraryConfig: {
receiveLibrary: "0x75Db67CDab2824970131D5aa9CECfC9F69c69636",
gracePeriod: 0,
},
sendConfig: {
executorConfig: {
maxMessageSize: 10000,
executor: "0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897",
},
ulnConfig: {
confirmations: 1,
requiredDVNs: ["0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8"],
optionalDVNs: [],
optionalDVNThreshold: 0,
},
},
// receiveConfig: {
// ulnConfig: {
// confirmations: 2,
// requiredDVNs: ['0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8'],
// optionalDVNs: [],
// optionalDVNThreshold: 0,
// },
// },
},
},
{
from: sepoliaContract,
to: arbsepContract,
config: {
sendLibrary: "0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE",
receiveLibraryConfig: {
receiveLibrary: "0xdAf00F5eE2158dD58E0d3857851c432E34A3A851",
gracePeriod: 0,
},
// sendConfig: {
// executorConfig: { maxMessageSize: 10000, executor: '0x718B92b5CB0a5552039B593faF724D182A881eDA' },
// ulnConfig: {
// confirmations: 2,
// requiredDVNs: ['0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193'],
// optionalDVNs: [],
// optionalDVNThreshold: 0,
// },
// },
receiveConfig: {
ulnConfig: {
confirmations: 1,
requiredDVNs: ["0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193"],
optionalDVNs: [],
optionalDVNThreshold: 0,
},
},
},
},
],
};
pnpm hardhat lz:oapp:config:wire --oapp-config YOUR_OAPP_CONFIG
Calls the configuration functions between your deployed OApp contracts on every chain based on the provided layerzero.config.ts
.
Running lz:oapp:wire
will make the following function calls per pathway connection for a fully defined config file using your specified settings and your environment variables (Private Keys and RPCs):
To use this task, run:
pnpm hardhat lz:oapp:wire --oapp-config YOUR_LAYERZERO_CONFIG_FILE
Whenever you make changes to the configuration, run lz:oapp:wire
again. The task will check your current configuration, and only apply NEW changes.
pnpm hardhat lz:oapp:config:get --oapp-config YOUR_OAPP_CONFIG
Returns your current OApp's configuration for each chain and pathway in 3 columns:
-
Custom Configuration: the changes that your
layerzero.config.ts
currently has set -
Default Configuration: the default placeholder configuration that LayerZero provides
-
Active Configuration: the active configuration that applies to the message pathway (Defaults + Custom Values)
If you do NOT explicitly set each configuration parameter, your OApp will fallback to the placeholder parameters in the default config.
ββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β Custom OApp Config β Default OApp Config β Active OApp Config β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β localNetworkName β arbsep β arbsep β arbsep β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β remoteNetworkName β sepolia β sepolia β sepolia β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β sendLibrary β 0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E β 0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E β 0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β receiveLibrary β 0x75Db67CDab2824970131D5aa9CECfC9F69c69636 β 0x75Db67CDab2824970131D5aa9CECfC9F69c69636 β 0x75Db67CDab2824970131D5aa9CECfC9F69c69636 β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β sendUlnConfig β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β confirmations β 1 β β β confirmations β 1 β β β confirmations β 1 β β
β β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β requiredDVNs β βββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β β β requiredDVNs β βββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β β β requiredDVNs β βββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β 0 β 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 β β β β β β 0 β 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 β β β β β β 0 β 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 β β β
β β β β βββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β β β β βββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β β β β βββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β
β β β β β β β β β β β β β β
β β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β optionalDVNs β β β β optionalDVNs β β β β optionalDVNs β β β
β β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β optionalDVNThreshold β 0 β β β optionalDVNThreshold β 0 β β β optionalDVNThreshold β 0 β β
β β ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β sendExecutorConfig β ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β
β β β executor β 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 β β β executor β 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 β β β executor β 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 β β
β β ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β maxMessageSize β 10000 β β β maxMessageSize β 10000 β β β maxMessageSize β 10000 β β
β β ββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β
β β β β β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β receiveUlnConfig β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β confirmations β 2 β β β confirmations β 2 β β β confirmations β 2 β β
β β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β requiredDVNs β βββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β β β requiredDVNs β βββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β β β requiredDVNs β βββββ¬βββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β 0 β 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 β β β β β β 0 β 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 β β β β β β 0 β 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 β β β
β β β β βββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β β β β βββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β β β β βββββ΄βββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β β β β β β β β β β β
β β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β optionalDVNs β β β β optionalDVNs β β β β optionalDVNs β β β
β β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β optionalDVNThreshold β 0 β β β optionalDVNThreshold β 0 β β β optionalDVNThreshold β 0 β β
β β ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β β
ββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
pnpm hardhat lz:oapp:config:get:executor --oapp-config YOUR_OAPP_CONFIG
Returns the LayerZero Executor config for each network in your hardhat.config.ts
. You can use this method to see the max destination gas in wei (nativeCap
) you can request in your execution options
.
βββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β localNetworkName β mantle β
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€
β remoteNetworkName β polygon β
βββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€
β executorDstConfig β ββββββββββββββββββ¬ββββββββββββββββββββββββ β
β β β baseGas β 85000 β β
β β ββββββββββββββββββΌββββββββββββββββββββββββ€ β
β β β multiplierBps β 12000 β β
β β ββββββββββββββββββΌββββββββββββββββββββββββ€ β
β β β floorMarginUSD β 5000000000000000000 β β
β β ββββββββββββββββββΌββββββββββββββββββββββββ€ β
β β β nativeCap β 681000000000000000000 β β
β β ββββββββββββββββββ΄ββββββββββββββββββββββββ β
β β β
βββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββ
This section only applies if you would like to configure manually instead of using the Simple Config Generator.
Define the pathway you want to create from and to each contract:
connections: [
// ETH <--> ARB PATHWAY: START
{
from: ethereumContract,
to: arbitrumContract,
},
{
from: arbitrumContract,
to: ethereumContract,
},
// ETH <--> ARB PATHWAY: END
];
Finally, define the config settings for each direction of the pathway:
connections: [
// ETH <--> ARB PATHWAY: START
{
from: ethereumContract,
to: arbitrumContract,
config: {
sendLibrary: contractsConfig.ethereum.sendLib302,
receiveLibraryConfig: {
receiveLibrary: contractsConfig.ethereum.receiveLib302,
gracePeriod: BigInt(0),
},
// Optional Receive Library Timeout for when the Old Receive Library Address will no longer be valid
receiveLibraryTimeoutConfig: {
lib: "0x0000000000000000000000000000000000000000",
expiry: BigInt(0),
},
// Optional Send Configuration
// @dev Controls how the `from` chain sends messages to the `to` chain.
sendConfig: {
executorConfig: {
maxMessageSize: 10000,
// The configured Executor address
executor: contractsConfig.ethereum.executor,
},
ulnConfig: {
// The number of block confirmations to wait on Ethereum before emitting the message from the source chain.
confirmations: BigInt(15),
// The address of the DVNs you will pay to verify a sent message on the source chain ).
// The destination tx will wait until ALL `requiredDVNs` verify the message.
requiredDVNs: [
contractsConfig.ethereum.horizenDVN, // Horizen
contractsConfig.ethereum.polyhedraDVN, // Polyhedra
contractsConfig.ethereum.animocaBlockdaemonDVN, // Animoca-Blockdaemon (only available on ETH <-> Arbitrum One)
contractsConfig.ethereum.lzDVN, // LayerZero Labs
],
// The address of the DVNs you will pay to verify a sent message on the source chain ).
// The destination tx will wait until the configured threshold of `optionalDVNs` verify a message.
optionalDVNs: [],
// The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified.
optionalDVNThreshold: 0,
},
},
// Optional Receive Configuration
// @dev Controls how the `from` chain receives messages from the `to` chain.
receiveConfig: {
ulnConfig: {
// The number of block confirmations to expect from the `to` chain.
confirmations: BigInt(20),
// The address of the DVNs your `receiveConfig` expects to receive verifications from on the `from` chain ).
// The `from` chain's OApp will wait until the configured threshold of `requiredDVNs` verify the message.
requiredDVNs: [
contractsConfig.ethereum.lzDVN, // LayerZero Labs DVN
contractsConfig.ethereum.animocaBlockdaemonDVN, // Blockdaemon-Animoca
contractsConfig.ethereum.horizenDVN, // Horizen Labs
contractsConfig.ethereum.polyhedraDVN, // Polyhedra
],
// The address of the `optionalDVNs` you expect to receive verifications from on the `from` chain ).
// The destination tx will wait until the configured threshold of `optionalDVNs` verify the message.
optionalDVNs: [],
// The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified.
optionalDVNThreshold: 0,
},
},
// Optional Enforced Options Configuration
// @dev Controls how much gas to use on the `to` chain, which the user pays for on the source `from` chain.
enforcedOptions: [
{
msgType: 1,
optionType: ExecutorOptionType.LZ_RECEIVE,
gas: 65000,
value: 0,
},
{
msgType: 2,
optionType: ExecutorOptionType.LZ_RECEIVE,
gas: 65000,
value: 0,
},
{
msgType: 2,
optionType: ExecutorOptionType.COMPOSE,
index: 0,
gas: 50000,
value: 0,
},
],
},
},
{
from: arbitrumContract,
to: ethereumContract,
},
// ETH <--> ARB PATHWAY: END
];
You can verify EVM chain contracts using the LayerZero helper package:
pnpm dlx @layerzerolabs/verify-contract -n <NETWORK_NAME> -u <API_URL> -k <API_KEY> --contracts <CONTRACT_NAME>
Refer to Debugging Messages or Error Codes & Handling.