Project Configuration
When working with a LayerZero project, it searches for the closest hardhat.config.ts
and layerzero.config.ts
files starting from the Current Working Directory. This file normally lives in the root of your project.
Modifying Hardhat Config
After initializing the repo, you will need to modify your hardhat.config.ts
with the expected networks you will be working with:
// hardhat.config.ts
networks: {
'optimism-testnet': {
eid: EndpointId.OPTSEP_V2_TESTNET,
url: process.env.RPC_URL_OP_SEPOLIA || 'https://optimism-sepolia.gateway.tenderly.co',
accounts,
},
'avalanche-testnet': {
eid: EndpointId.AVALANCHE_V2_TESTNET,
url: process.env.RPC_URL_FUJI || 'https://avalanche-fuji.drpc.org',
accounts,
},
'arbitrum-testnet': {
eid: EndpointId.ARBSEP_V2_TESTNET,
url: process.env.RPC_URL_ARB_SEPOLIA || 'https://arbitrum-sepolia.gateway.tenderly.co',
accounts,
},
},
The only notable change from a standard hardhat.config.ts
setup is the inclusion of a LayerZero Endpoint ID.
For hardhat specific questions, refer to the Hardhat Configuration documentation.
The npx package uses @layerzerolabs/lz-definitions
to enable you to reference both V1 and V2 Endpoints. Make sure if your project uses LayerZero V2 to select the V2 Endpoint (i.e., eid: EXAMPLE_V2_MAINNET
).
Modifying LayerZero Config
The layerzero.config.ts
first defines what contracts you expect to deploy on each network, using the @layerzerolabs/lz-definitions
package as a mapping for each network:
// layerzero.config.ts
import {EndpointId} from '@layerzerolabs/lz-definitions';
// Define the Ethereum contract
// eid specifies the network (LZ V2 Ethereum Sepolia Testnet)
// contractName is the name of the contract.
const sepoliaContract = {
eid: EndpointId.SEPOLIA_V2_TESTNET,
contractName: 'MyOFT',
};
// Define the Binance contract
// eid specifies the network (LZ V2 BNB Chain Testnet)
// contractName is the name of the contract.
const bscContract = {
eid: EndpointId.BSC_V2_TESTNET,
contractName: 'MyOFT',
};
// Define the Amoy (Polygon) contract
// eid specifies the network (LZ V2 Polygon Amoy Testnet)
// contractName is the name of the contract.
const amoyContract = {
eid: EndpointId.AMOY_V2_TESTNET,
contractName: 'MyOFT',
};
After defining what contracts to use on each network, you can specify which contracts should be connected on a per pathway basis:
// layerzero.config.ts
import {EndpointId} from '@layerzerolabs/lz-definitions';
import {ExecutorOptionType} from '@layerzerolabs/lz-v2-utilities';
import {TwoWayConfig, generateConnectionsConfig} from '@layerzerolabs/metadata-tools';
import {OAppEnforcedOption, OmniPointHardhat} from '@layerzerolabs/toolbox-hardhat';
const optimismContract: OmniPointHardhat = {
eid: EndpointId.OPTSEP_V2_TESTNET,
contractName: 'MyOApp',
};
const avalancheContract: OmniPointHardhat = {
eid: EndpointId.AVALANCHE_V2_TESTNET,
contractName: 'MyOApp',
};
const arbitrumContract: OmniPointHardhat = {
eid: EndpointId.ARBSEP_V2_TESTNET,
contractName: 'MyOApp',
};
// For this example's simplicity, we will use the same enforced options values for sending to all chains
// For production, you should ensure `gas` is set to the correct value through profiling the gas usage of calling OApp._lzReceive(...) on the destination chain
// To learn more, read https://docs.layerzero.network/v2/concepts/applications/oapp-standard#execution-options-and-enforced-settings
const EVM_ENFORCED_OPTIONS: OAppEnforcedOption[] = [
{
msgType: 1,
optionType: ExecutorOptionType.LZ_RECEIVE,
gas: 80000,
value: 0,
},
];
// To connect all the above chains to each other, we need the following pathways:
// Optimism <-> Avalanche
// Optimism <-> Arbitrum
// Avalanche <-> Arbitrum
// With the config generator, pathways declared are automatically bidirectional
// i.e. if you declare A,B there's no need to declare B,A
const pathways: TwoWayConfig[] = [
[
optimismContract, // Chain A contract
avalancheContract, // Chain B contract
[['LayerZero Labs'], []], // [ requiredDVN[], [ optionalDVN[], threshold ] ]
[1, 1], // [A to B confirmations, B to A confirmations]
[EVM_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Chain B enforcedOptions, Chain A enforcedOptions
],
[
optimismContract, // Chain A contract
arbitrumContract, // Chain C contract
[['LayerZero Labs'], []], // [ requiredDVN[], [ optionalDVN[], threshold ] ]
[1, 1], // [A to B confirmations, B to A confirmations]
[EVM_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Chain C enforcedOptions, Chain A enforcedOptions
],
[
avalancheContract, // Chain B contract
arbitrumContract, // Chain C contract
[['LayerZero Labs'], []], // [ requiredDVN[], [ optionalDVN[], threshold ] ]
[1, 1], // [A to B confirmations, B to A confirmations]
[EVM_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Chain C enforcedOptions, Chain B enforcedOptions
],
];
export default async function () {
// Generate the connections config based on the pathways
const connections = await generateConnectionsConfig(pathways);
return {
contracts: [
{contract: optimismContract},
{contract: avalancheContract},
{contract: arbitrumContract},
],
connections,
};
}
Checking Pathway Configurations
To check your OApp's current configuration, you can run:
npx hardhat lz:oapp:config:get --oapp-config layerzero.config.ts
This command will output a table with 3 columns:
-
Custom OApp Config: your
layerzero.config.ts
configuration changes, with null values for unchanged parameters. -
Default OApp Config: the default LayerZero configuration for the pathway.
-
Active OApp Config: the combination of your customized and default parameters, i.e., the active configuration.
┌────────────────────┬─────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────── ──────────────────────────────┬───────────────────────────────────────────────────────────────────────────────┐
│ │ Custom OApp Config │ Default OApp Config │ Active OApp Config │
├────────────────────┼─────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ localNetworkName │ bsc_testnet │ bsc_testnet │ bsc_testnet │
├────────────────────┼─────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ remoteNetworkName │ sepolia │ sepolia │ sepolia │
├────────────────────┼─────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ sendLibrary │ 0x0000000000000000000000000000000000000000 │ 0x55f16c442907e86D764AFdc2a07C2de3BdAc8BB7 │ 0x55f16c442907e86D764AFdc2a07C2de3BdAc8BB7 │
├────────────────────┼─────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ receiveLibrary │ 0x0000000000000000000000000000000000000000 │ 0x188d4bbCeD671A7aA2b5055937F79510A32e9683 │ 0x188d4bbCeD671A7aA2b5055937F79510A32e9683 │
├────────────────────┼─────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────── ───────────────────────────────────────┤
│ sendUlnConfig │ ┌──────────────────────┬───┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │
│ │ │ confirmations │ 0 │ │ │ confirmations │ 5 │ │ │ confirmations │ 5 │ │
│ │ ├──────────────────────┼───┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼───────── ───────────────────────────────────────────┤ │
│ │ │ requiredDVNs │ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │
│ │ ├──────────────────────┼───┤ │ │ │ │ 0 │ 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 │ │ │ │ │ │ 0 │ 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 │ │ │
│ │ │ optionalDVNs │ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │
│ │ ├─────────────── ───────┼───┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │
│ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNs │ │ │ │ optionalDVNs │ │ │
│ │ └──────────────────────┴───┘ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │
│ │ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNThreshold │ 0 │ │
│ │ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ └──────────────────────┴────────────────────────────────────────────────────┘ │
├────────────────────┼─────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ sendExecutorConfig │ ┌────────────────┬────────────────────────────────────────────┐ │ ┌───────── ───────┬────────────────────────────────────────────┐ │ ┌────────────────┬────────────────────────────────────────────┐ │
│ │ │ executor │ 0x0000000000000000000000000000000000000000 │ │ │ executor │ 0x31894b190a8bAbd9A067Ce59fde0BfCFD2B18470 │ │ │ executor │ 0x31894b190a8bAbd9A067Ce59fde0BfCFD2B18470 │ │
│ │ ├────────────────┼────────────────────────────────────────────┤ │ ├────────────────┼────────────────────────────────────────────┤ │ ├────────────────┼────────────────────────────────────────────┤ │
│ │ │ maxMessageSize │ 0 │ │ │ maxMessageSize │ 10000 │ │ │ maxMessageSize │ 10000 │ │
│ │ └────────────────┴────────────────────────────────────────────┘ │ └────────────────┴────────────────────────────────────────────┘ │ └────────────────┴────────────────────────────────────────────┘ │
├────────────────────┼─────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
│ receiveUlnConfig │ ┌──────────────────────┬───┐ │ ┌──────────────────────┬───────────────────────────────── ───────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │
│ │ │ confirmations │ 0 │ │ │ confirmations │ 2 │ │ │ confirmations │ 2 │ │
│ │ ├──────────────────────┼───┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │
│ │ │ requiredDVNs │ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │
│ │ ├──────────────────────┼───┤ │ │ │ │ 0 │ 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 │ │ │ │ │ │ 0 │ 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 │ │ │
│ │ │ optionalDVNs │ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │
│ │ ├──────────────────────┼───┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────── ──┤ │
│ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNs │ │ │ │ optionalDVNs │ │ │
│ │ └──────────────────────┴───┘ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │
│ │ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNThreshold │ 0 │ │
│ │ │ └──────────────────────┴───────────────────── ───────────────────────────────┘ │ └──────────────────────┴────────────────────────────────────────────────────┘ │
└────────────────────┴─────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┘
Adding Pathway Configurations
To add specific configurations on a per pathway basis, review the Configuring Pathways section.