Template project for creating custom omnichain applications (OApp) powered by the LayerZero protocol. This example demonstrates how to build applications that can send and receive arbitrary messages across different blockchains.
- Prerequisite Knowledge
- Requirements
- Scaffold this example
- Helper Tasks
- Setup
- Build
- Deploy
- Enable Messaging
- Sending Messages
- Next Steps
- Production Deployment Checklist
- Appendix
Node.js->=18.16.0pnpm(recommended) - or another package manager of your choice (npm, yarn)forge(optional) ->=0.2.0for testing, and if not using Hardhat for compilation
Create your local copy of this example:
pnpm dlx create-lz-oapp@latest --example oappSpecify the directory, select OApp 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.exampleinto a new.env -
Set up your deployer address/account via the
.env-
You can specify either
MNEMONICorPRIVATE_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 Sepolia and Arbitrum Sepolia.
This project supports both hardhat and forge compilation. By default, the compile command will execute both:
pnpm compileIf you prefer one over the other, you can use the tooling-specific commands:
pnpm compile:forge
pnpm compile:hardhatTo deploy the OApp contracts to your desired blockchains, run the following command:
pnpm hardhat lz:deploy --tags SavioLZManagerSelect all the chains you want to deploy the OApp to.
After deploying the OApp on the respective chains, you enable messaging by running the wiring task.
ℹ️ This example uses the Simple Config Generator, which is recommended over manual configuration.
Run the wiring task:
pnpm hardhat lz:oapp:wire --oapp-config layerzero.config.tsSubmit all the transactions to complete wiring. After all transactions confirm, your OApps are wired and can send messages to each other.
With your OApps wired, you can now send messages cross-chain.
Send a message from Optimism Sepolia to Arbitrum Sepolia:
pnpm hardhat lz:oapp:send --dst-eid 40231 --string 'Hello from Ethereum!' --network optimism-testnetSend a message from Arbitrum Sepolia to Optimism Sepolia:
pnpm hardhat lz:oapp:send --dst-eid 40161 --string 'Hello from Arbitrum!' --network arbitrum-testnetℹ️
40161and40231are the Endpoint IDs of Optimism Sepolia and Arbitrum Sepolia respectively. The source network is determined by the--networkflag, not a separate--src-eidparameter. 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 message was received.
Congratulations, you have now sent a message 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:
- (recommended) you have profiled the gas usage of
lzReceiveon your destination chains - (recommended) you have configured appropriate DVNs for your security requirements
- (recommended) you have tested your application thoroughly on testnets
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 testIf you prefer one over the other, you can use the tooling-specific commands:
pnpm test:forge
pnpm test:hardhatIf 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
eidandcontractName) - decide whether to use an existing EVM enforced options variable or declare a new one
- create a new entry in the
connectionsarray - add the new contract into the
contractsarray of theexport defaultfunction
After applying the desired changes, make sure you re-run the wiring task:
pnpm hardhat lz:oapp:wire --oapp-config layerzero.config.tsThe 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 interact with OApps 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.
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 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_FILEWhenever 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.tscurrently 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.
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.