Skip to main content

Getting Started

To start sending omnichain messages with LayerZero, you only need to implement two functions:

  • _lzSend: an inherited function that your application calls to send an omnichain message.

    _dstEid, // the destination endpoint id
    _payload, // encoded message payload being sent
    _options, // message execution options
    MessagingFee(msg.value, 0), // the fee in native gas and ZRO token
    payable(msg.sender) // refund address in case of failed source message
  • _lzReceive: a handling function for what your application should do after receiving a message.

    function _lzReceive(
    Origin calldata _origin, // struct containing srcEid, sender address, and the message nonce
    bytes32 _guid, // global message packet identifier
    bytes calldata payload, // encoded message being received
    address _executor, // the address of who executed the message
    bytes calldata _extraData // appended executor data for the call
    ) internal override {
    data = abi.decode(payload, (string)); // your receive logic here

LayerZero offers Contract Standards that simplify this implementation by providing out of the box message handling, interfaces for custom protocol configurations, and other quality of life improvements:

  • OApp: the base contract standard for omnichain messaging and configuration.

  • OFT: an extension of OApp built for handling and supporting omnichain fungible token transfers.

Example Omnichain Application

Inside the OApp contract standard, you will find a send and receive interface. These interfaces can be used directly if an application only needs messaging in a single direction.

You can use the Remix IDE to deploy an example OAppSender and OAppReceiver contract to see how to send and store a simple string across different chains.

First, open the OAppSender.sol contract and compile it:

Open in RemixWhat is Remix?

Next, open the OAppReceiver.sol contract:

Open in RemixWhat is Remix?


  1. You should first be familiar with writing and deploying contracts to your desired blockchains. This involves understanding the specific smart contract language and the deployment process for those chains.

  2. A wallet set up and funded for the chains you'll be working with.

Deploying Your Contracts

We'll deploy the Source Contract on Sepolia, and the Destination Contract on Optimism Sepolia:


This example can be used with any EVM-compatible blockchain that LayerZero supports.

  1. Open MetaMask and select the Ethereum Sepolia network. Make sure you have native gas in the wallet connected.

  2. In Remix under the Deploy & Run Transactions tab, select Injected Provider - MetaMask in the Environment list.

  3. Under the Deploy section, fill in the Endpoint Address for your current chain.

  1. Click deploy, follow the MetaMask prompt to confirm the transaction, and wait for the contract address to appear under Deployed Contracts.

  2. Repeat the above steps for any other chains you plan to deploy to and connect.

Connecting Your Contracts

To connect your OApp deployments together, you will need to call setPeer on both the Ethereum Sepolia and Optimism Sepolia OApp.

The function takes 2 arguments: _eid, the destination endpoint ID for the chain our other OApp contract lives on, and _peer, the destination OApp contract address in bytes32 format.

// @dev must-have configurations for standard OApps
function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {
peers[_eid] = _peer; // Array of peer addresses by destination.
emit PeerSet(_eid, _peer); // Event emitted each time a peer is set.

Remember, an EVM address is a bytes20 value. That means you will need to convert your address to bytes32 when calling setPeer. This can easily be done by Zero Padding the address until it is 32 bytes in length.

LayerZero uses bytes32 for broad compatibility with non-EVM chains.

Pass the address of your destination contract as a bytes32 value, as well as the destination endpoint ID.

  • To send to Ethereum Sepolia, the Endpoint ID is: 40161.

  • To send to Optimism Sepolia, the Endpoint ID is: 40232.


You'll need to repeat this wiring on both contracts in order to send and receive messages. That means calling setPeer on both your Ethereum Sepolia and Optimism Sepolia contracts. Remember to switch networks in MetaMask.

If successful, you now should be setup to start sending cross-chain messages!

Estimating Fees

The LayerZero Protocol gas fees can vary based on your source chain, destination chain, and the payload you're attempting to send, which is why we recommend estimating fees before sending your first transaction.

To do this, we'll use the quote function.

This function invokes the _quote function to estimate the fees associated with a particular LayerZero transaction using four inputs:

  • _dstEid: This is the identifier of the destination chain's endpoint where the transaction is intended to go.

  • _message: This is the arbitrary message you intend to send to your destination chain and contract.

  • _options: A bytes array that contains serialized execution options that tell the protocol the amount of gas to for the Executor to send when calling lzReceive, as well as other function call related settings.

  • _payInLzToken: A boolean which determines whether to return the fee estimate in the native gas token or in ZRO token.


In this tutorial, you will deliver 200000 wei for the lzReceive call by passing 0x00030100110100000000000000000000000000030d40 as your _options. You will be quoted 200000 wei on the source chain, which the Executor will convert to the destination gas token and use in the call. See Message Execution Options for all possible execution settings.

Sending Your Message

To use the send function, simply input a string into the message field that you wish to send to your destination chain.

Contract A

Remember to pass the msg.value we quoted using quote in Remix, as we still need to pay gas fees on the source and destination, as well as for the Security Stack and Executor who authenticate and deliver the messages. Once you've successfully sent your transaction, call the data field from your destination contract to see your first omnichain message!

Contract B

Your message may take a few minutes to appear in the destination block explorer, depending on which chains you deploy to.

Tracking Your Message

Finally, let's see what's happening in our transaction. Take your transaction hash and paste it into:

You should see Status: Delivered, confirming your message has been delivered to its destination using LayerZero.

Congrats, you just sent your first omnichain message! 🥳