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).
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:
- OApp
- OFT
- OFT Adapter
- ONFT
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
An ERC20 extended with core bridging logic from OApp, creating an Omnichain Fungible Token (OFT):
✔ Where do you want to start your project? … ./my-lz-oapp
✔ Which example would you like to use as a starting point? › OFT
✔ What package manager would you like to use in your project? › pnpm
Variant of OFT for adapting deployed ERC20 tokens as Omnichain Fungible Tokens, creating an OFT Adapter:
✔ Where do you want to start your project? … ./my-lz-oapp
✔ Which example would you like to use as a starting point? › OFT Adapter
✔ What package manager would you like to use in your project? › pnpm
An ERC721 extended with core bridging logic from OApp, creating an Omnichain Non-Fungible Token (ONFT):
✔ Where do you want to start your project? … ./my-lz-oapp
✔ Which example would you like to use as a starting point? › ONFT
✔ 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>