> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Getting Started with Contract Standards

> Get started with Getting Started with Contract Standards. Step-by-step tutorial for building omnichain applications on LayerZero V2. LayerZero enables...

export const Button = ({variant = 'primary', href, target, children}) => {
  const baseClass = "custom-button";
  const variantClass = variant === 'secondary' ? 'custom-button-secondary' : 'custom-button-primary';
  const combinedClass = `${baseClass} ${variantClass}`;
  if (href) {
    return <a href={href} target={target} rel={target === '_blank' ? 'noreferrer' : undefined} className={combinedClass}>
        {children}
      </a>;
  }
  return <button className={combinedClass}>
      {children}
    </button>;
};

Use LayerZero's **Contract Standards** to easily start sending arbitrary data, tokens, and external calls using the protocol:

* [Omnichain Application (OApp)](./oapp/overview): the base contract standard for omnichain messaging and configuration.

* [Omnichain Fungible Token (OFT)](./oft/quickstart): an extension of `OApp` built for handling and supporting omnichain `ERC20` transfers.

* [Omnichain Non-Fungible Token (ONFT)](./onft/quickstart): an extension built for handling and supporting omnichain `ERC721` transfers.

Each of these contract standards implement common functions for **sending**, **receiving**, and **configuring** omnichain messages via the protocol interface: the [LayerZero Endpoint](../../concepts/protocol/layerzero-endpoint) contract.

* `OAppSender._lzSend`: internal function that calls `EndpointV2.send` to send a message as `bytes`.

* `OAppReceiver._lzReceive`: internal function that delivers the encoded message as `bytes` after the `Executor` calls `EndpointV2.lzReceive`.

This method of **encoding** send parameters and **decoding** them on the destination chain is the basis for how all OApps work.

## Example Omnichain Application

The `OApp` Standard contains both a **send** and **receive** interface.

<Info>
  This code snippet is already implemented in the Remix example below. Simply review this code to understand how it works internally.
</Info>

```solidity wrap theme={null}
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers
import { OAppSender, MessagingFee, MessagingReceipt } from "./OAppSender.sol";
// @dev Import the 'Origin' so it's exposed to OApp implementers
import { OAppReceiver, Origin } from "./OAppReceiver.sol";
import { OAppCore } from "./OAppCore.sol";

/**
 * @title OApp
 * @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.
 */
// highlight-next-line
abstract contract OApp is OAppSender, OAppReceiver {}
```

You can use the **Remix IDE** to see how `OAppSender` and `OAppReceiver` work together for sending and receiving any arbitrary data to supported destination chains.

### OAppSender.sol

<Button variant="primary" href="https://remix.ethereum.org/#url=https://docs.layerzero.network/LayerZero/contracts/Source.sol" target="_blank">Open in Remix</Button>

<Button variant="secondary" href="https://remix-ide.readthedocs.io/en/latest/index.html" target="_blank">What is Remix?</Button>

#### OAppReceiver.sol

<Button variant="primary" href="https://remix.ethereum.org/#url=https://docs.layerzero.network/LayerZero/contracts/Destination.sol" target="_blank">Open in Remix</Button>

<Button variant="secondary" href="https://remix-ide.readthedocs.io/en/latest/index.html" target="_blank">What is Remix?</Button>

### Prerequisites

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`:

<Info>
  This example can be used with any EVM-compatible blockchain that LayerZero supports.
</Info>

<br />

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](../../deployments/deployed-contracts) for your current chain.

#### Sepolia Endpoint Address

```
0x6edce65403992e310a62460808c4b910d972f10f
```

#### Optimism Sepolia Endpoint Address

```
0x6edce65403992e310a62460808c4b910d972f10f
```

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

5. 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.

```solidity wrap theme={null}
// LayerZero/V2/oapp/contracts/oapp/OAppReceiver.sol
// @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.
}
```

To `setPeer` on `SourceOApp`, take the `DestinationOApp` address and call `OApp.addressToBytes32`. Use the returned output as the `_peer`.

Your `_peer` should look something like this: `0x0000000000000000000000000a3ecc421699e2eb7f53584d07165d95721a4ca7`.

By default, the `OApp` standard inherits `OAppReceiver` which uses this peer inside `lzReceive` to enforce that the sender is the expected origin address.

```solidity wrap theme={null}
// LayerZero/V2/oapp/contracts/oapp/OAppCore.sol

/**
 * @dev Entry point for receiving messages or packets from the endpoint.
 * @param _origin The origin information containing the source endpoint and sender address.
 *  - srcEid: The source chain endpoint ID.
 *  - sender: The sender address on the src chain.
 *  - nonce: The nonce of the message.
 * @param _guid The unique identifier for the received LayerZero message.
 * @param _message The payload of the received message.
 * @param _executor The address of the executor for the received message.
 * @param _extraData Additional arbitrary data provided by the corresponding executor.
 *
 * @dev Entry point for receiving msg/packet from the LayerZero endpoint.
 */
function lzReceive(
    Origin calldata _origin,
    bytes32 _guid,
    bytes calldata _message,
    address _executor,
    bytes calldata _extraData
) public payable virtual {
    // Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.
    if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);

    // Ensure that the sender matches the expected peer for the source endpoint.
    if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);

    // Call the internal OApp implementation of lzReceive.
    _lzReceive(_origin, _guid, _message, _executor, _extraData);
}
```

<Tip>
  Remember, an EVM `address` is a `bytes20` value, so you must convert your address to `bytes32` when calling `setPeer`. This can also be easily be done by [**Zero Padding**](https://ethereum.stackexchange.com/questions/103901/can-you-convert-my-address-bytes20-type-to-a-bytes32-string) the address until it is 32 bytes in length.

  LayerZero uses `bytes32` for broad compatibility with non-EVM chains.
</Tip>

<br />

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`.

<Warning>
  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.**
</Warning>

If successful, you now should be setup to start sending crosschain messages!

### Estimating Fees

The LayerZero Protocol gas fees can vary based on your source chain, `DVNs`, `Executor`, and amount of native gas token you request in `_options`, so you should estimate fees before sending your first transaction.

The `OApp.quote` function invokes an internal `OAppSender._quote` 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](../../concepts/permissionless-execution/executors) 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.

<Info>
  In this tutorial, you will deliver `50000` wei for the `lzReceive` call by passing `0x0003010011010000000000000000000000000000c350` as your `_options`. You will be quoted `50000` wei on the source chain, which the Executor will convert to the destination gas token and use in the call. See [**Message Execution Options**](/v2/developers/evm/configuration/options) for all possible execution settings.
</Info>

### 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 `quote` in Remix under `VALUE` to pay the gas fees on the source and destination, as well as for the [Security Stack](../../concepts/modular-security/security-stack-dvns) and Executor who verify and execute the messages. Then call `SourceOApp.send`!

#### 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: [https://testnet.layerzeroscan.com/](https://testnet.layerzeroscan.com/)

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

**Congrats, you just sent your first omnichain message!** 🥳

### Further Reading

Now that you understand the basics for how OApps work, you should explore setting up your development environment and diving deeper into the omnichain contract standards!

* [Create LZ OApp Quickstart](../../get-started/create-lz-oapp/start)

* [OApp Quickstart](../../developers/evm/oapp/overview)

* [OFT Quickstart](../../developers/evm/oft/quickstart)
