Skip to main content
Version: Endpoint V2 Docs

Create LZ OApp Quickstart

LayerZero provides create-lz-oapp, a CLI Toolkit designed to streamline the process of building, testing, deploying, and configuring omnichain applications (OApps).

tip

Get started by running the following from your command line:

npx create-lz-oapp@latest

create-lz-oapp is an npx package that creates a Node.js project with both the Hardhat and Foundry development frameworks installed, allowing developers to build from any LayerZero Contract Standards.

Start a simple, barebones dApp, to a feature-rich cross-chain platform, in <4 minutes!

Setting Up Your LayerZero Project

Run npx create-lz-oapp@latest to bootstrap your initial LayerZero project:

npx create-lz-oapp@latest

Following this, a simple project creation wizard will guide you through setting up a project template. Choose from a variety of examples to match your project needs:

The generic message passing standard for creating Omnichain Applications (OApps):

✔ Where do you want to start your project? … ./my-lz-oapp
✔ Which example would you like to use as a starting point? › OApp
✔ What package manager would you like to use in your project? › pnpm

This will initialize a repo with example contracts, cross-chain unit tests for sample contracts, custom LayerZero configuration files, deployment scripts, and more.

Sample Project Overview

The initialized project has the following structure:

contracts / // your contracts folder
deploy / // hardhat-deploy scripts
test / // unit-tests, both hardhat and foundry enabled
foundry.toml; // normal foundry.toml for remappings and project configuration
hardhat.config.ts; // standard hardhat.config.ts, with layerzero endpoint mappings
layerzero.config.ts; // special LayerZero config file (more on this later)

If you need to change these paths, refer to the hardhat paths configuration documentation.

To see all of the available LayerZero hardhat tasks, you can run:

$ npx hardhat
Hardhat version 2.22.15

AVAILABLE TASKS:

// ... hardhat defaults

lz:deploy Deploy LayerZero contracts
lz:errors:decode Decodes custom error data based
lz:errors:list List all custom errors from your project
lz:oapp:config:get Outputs Custom OApp Config, Default OApp Config, and Active OApp Config. Each config contains Send & Receive Libraries, Send Uln & Executor Configs, and Recieve Executor Configs
lz:oapp:config:get:default Outputs the Default OApp Config. Each config contains Send & Receive Libraries, Send Uln & Executor Configs, and Receive Executor Configs
lz:oapp:config:get:executor Outputs the Executors destination configurations including the native max cap amount
lz:oapp:enforced-opts:get Outputs OApp enforced options
lz:oapp:peers:get Outputs OApp peer connections
lz:oapp:wire Wire LayerZero OApp

Adding External Networks

You will need to modify project configurations to add any new networks you plan to deploy on.

Follow the standard Hardhat process for adding external networks to your hardhat.config.ts, with the only additional requirement being that a LayerZero Endpoint has been deployed to the chain:

// hardhat.config.ts
networks: {
ethereum: {
eid: EndpointId.ETHEREUM_V2_MAINNET, // from @layerzerolabs/lz-definitions
url: process.env.RPC_URL_ETHEREUM || 'https://eth-pokt.nodies.app',
accounts,
},
arbitrum: {
eid: EndpointId.ARBITRUM_V2_MAINNET, // from @layerzerolabs/lz-definitions
url: process.env.RPC_URL_ARBITRUM || 'https://api.stateless.solutions/arbitrum-one/v1/demo',
accounts,
},
},

Review the list of LayerZero Endpoint Addresses to see which networks you can set.

Testing LayerZero Contracts

To begin testing your sample LayerZero contracts locally, you can use Hardhat and / or Foundry unit tests, with specific local testing utilities for each:

  • Hardhat: EndpointV2Mock.sol, a mock LayerZero V2 Endpoint contract, meant for local testing of LayerZero message passing in Hardhat.

  • Foundry: TestHelper.sol, an extensive LayerZero V2 testing framework, designed for simulating LayerZero state changes and contract interactions in Foundry.

As well as other useful plugins. You can learn more about this in Testing Contracts.

Deploying LayerZero Contracts

To deploy your LayerZero contracts, you can run:

npx hardhat lz:deploy

Using the network names defined in your hardhat.config.ts and the contract defined in your layerzero.config.ts.

See Deploying Contracts to learn more.

Initializing LayerZero Configs

To setup an initial layerzero.config.ts file, run:

npx hardhat lz:oapp:config:init --contract-name DEPLOYMENT_NAME --oapp-config CONFIG_FILE_NAME

This will auto-populate the provided config file, or create the file if the path does not exist, with the current LayerZero default configurations as a placeholder.

For example, running:

npx hardhat lz:oapp:config:init --contract-name MyOApp --oapp-config testnet.layerzero.config.ts

will create a new file in my directory with the correct type interface:

contracts/
deploy/
test/
foundry.toml
hardhat.config.ts
layerzero.config.ts
testnet.layerzero.config.ts <-----

To return the default configuration for all possible network pathways from your hardhat.config.ts in your terminal, run:

npx hardhat lz:oapp:config:get:default

Configuring LayerZero Contracts

LayerZero contracts have unique configurations on a per pathway basis (i.e., from A to B has different properties than from B to A).

For a detailed overview of all possible configuration commands, see Configuring Contracts.

To configure your LayerZero contracts, simply modify the config of the pathway in your layerzero.config.ts.

Once you have configured your contract pathways, you can wire them together using:

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

For each pathway in your config file, this task will call the following:

fromContract.OApp.setPeer
fromContract.EndpointV2.setSendLibrary
fromContract.EndpointV2.setReceiveLibrary
fromContract.EndpointV2.setConfig(OApp, sendLibrary, sendConfig)
fromContract.EndpointV2.setConfig(OApp, receiveLibrary, receiveConfig)

If you have manually added enforcedOptions and receiveLibraryTimeoutConfig, the following will also be called for each contract instance, applying the custom configurations added:

fromContract.OApp.setEnforcedOptions
fromContract.EndpointV2.setReceiveLibraryTimeout

This means that the pathway configurations should mirror, for example:

// connections[] in layerzero.config.ts
// configContracts is just a constants file with contract addresses
{
from: ethereumContract,
to: arbitrumContract,
// the config below is SET on Ethereum
config: {
sendConfig: {
ulnConfig: {
confirmations: BigInt(5), // block confirmations to wait for finality
optionalDVNThreshold: 0,
requiredDVNs: [ // DVNs to pay to verify
configContracts.ethereumContracts.lzDvn,
configContracts.ethereumContracts.nethermindDvn,
],
optionalDVNs: [],
},
},
},
},
{
from: arbitrumContract,
to: ethereumContract,
// the config below is SET on Arbitrum
config: {
receiveConfig: {
ulnConfig: {
confirmations: BigInt(5), // enforces DVNs waited for finality
optionalDVNThreshold: 0,
requiredDVNs: [ // enforces specific DVNs have verified
configContracts.arbitrumContracts.lzDvn,
configContracts.arbitrumContracts.nethermindDvn
],
optionalDVNs: [],
},
},
},
},

Remember to apply the opposite direction if the pathway is bi-directional (i.e., adding a sendConfig to from: arbitrumContract, and a receiveConfig to from: ethereumContract).

Debugging Contract Errors

To list all the custom errors defined in the LayerZero protocol and your project, run:

npx hardhat lz:errors:list

To help decode unknown error signatures when testing, run:

npx hardhat lz:errors:decode <error selector>