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

# Omnichain Composers

> Overview of Omnichain Composers on LayerZero V2. Learn the architecture, features, and how to get started building. LayerZero enables secure crosschain...

Crosschain composability has long been a goal for developers building advanced, interconnected decentralized applications.

LayerZero V2 introduces **horizontal composability** — a concept that empowers developers to spread out crosschain calls into multiple, discrete steps.

## Prerequisites

Before diving into LayerZero V2 Horizontal Composability, it's essential to have a foundational understanding of the following concepts:

* **[Solidity Interfaces](https://blog.paulmcaviney.ca/solidity-interfaces)**: Knowledge of defining and implementing interfaces in Solidity.

* **[Solidity Interface Composability](https://dev.to/shlok2740/interfaces-in-solidity-26m3#:~:text=Interfaces%20allow%20for%20composability%20between,any%20contract%20that%20implements%20it.)**: Grasping how interfaces facilitate composability between contracts.

Having familiarity with these topics will enable a smoother comprehension of the concepts discussed.

## Workflow

LayerZero V2 supports both **Vertical and Horizontal Composability** within crosschain calls.

### What is Vertical Composability?

**Vertical Composability** is the traditional model of composability in blockchain applications, where multiple function calls from different contracts are stacked within a single transaction.

```solidity wrap theme={null}
// Example of vertical composability with atomicity
function _lzReceive(
    Origin calldata /*_origin*/,
    bytes32 /*_guid*/,
    bytes calldata /*_message*/,
    address /*_executor*/,
    bytes calldata /*_extraData*/
) internal override {
    contractA.functionA();
    contractB.functionB();
    contractC.functionC();
    // If any of the above calls fail, the entire transaction reverts
}
```

All function calls in the stack execute atomically. This means that either all operations succeed, or the entire transaction reverts if any single operation fails.

<Warning>
  Vertical composability can present potential **Atomicity Issues** in crosschain interactions:

  * If an operation on one contract fails, it can produce unintended reversions or inconsistencies across the entire stack. This limits the ability to have instant finality guarantees when receiving crosschain messages.

  In crosschain contracts, you should minimize the impact of potential message failure by performing only one action per message.
</Warning>

### What is Horizontal Composability?

**Horizontal Composability** is an implementation in **LayerZero V2** to address the limitations of vertical composability in crosschain interactions.

Unlike vertical composability, which relies on a single, linear stack of function calls, horizontal composability allows for multiple, sequential calls across different chains within a single overarching operation.

This facilitates the orchestration of complex, multi-step interactions across multiple chains without being constrained by the depth or complexity of a single call stack.

### How Horizontal Composability Works

LayerZero's horizontal composability leverages composed messages that are treated as separate, containerized message packets. These packets are processed independently, allowing for more flexible and controlled interactions across chains.

**Workflow Overview:**

1. **Sending Application Logic:** The sender application uses the `OApp._lzSend()` function to dispatch a crosschain message.

2. **Receiving Application Logic:** A destination application receives the message from `EndpointV2.lzReceive()`, does some state change, and then calls `EndpointV2.sendCompose()` to send a new message to the target composer.

   <Info>
     Crucially, either the `sender` or `receiver` should construct an additional message directed at a `composer`, which will handle subsequent operations in a new method, `EndpointV2.lzCompose()`.

     This dual-message approach ensures that both the immediate and follow-up actions are clearly defined and routed appropriately.
   </Info>

3. **Composer Application Logic:** A composer application receives the composed message in `lzCompose()` and does a state change to follow up on the first state changes created in `lzReceive()`.

This workflow creates a way for delivering some critical state change information in separate steps, reducing the complexity of the call stack and enabling non-critical reverts on the destination chain.

### Horizontally Composing Supported Contracts

Implementing horizontal composability involves crafting composed messages to expand on existing crosschain contract workflows. By default, both the `OFT` and `ONFT` standards support horizontally composed calls out of the box.

This allows `OFT` or `ONFT` token holders to send tokens crosschain to a trusted `composer` contract on the destination, and trigger some action on behalf of the token holders (e.g., token swaps, token staking, etc).

For more advanced implementations, you can design complex `OApp` contracts that have other crosschain `composer` implications.

## Installation

To create a `composer` contract, you can install the [OApp package](https://www.npmjs.com/package/@layerzerolabs/oapp-evm) to an existing project:

<CodeGroup>
  ```bash wrap npm theme={null}
  npm install @layerzerolabs/oapp-evm
  ```

  ```bash wrap yarn theme={null}
  yarn add @layerzerolabs/oapp-evm
  ```

  ```bash wrap pnpm theme={null}
  pnpm add @layerzerolabs/oapp-evm
  ```

  ```bash wrap forge theme={null}
  forge install layerzero-labs/devtools --no-commit

  forge install layerzero-labs/LayerZero-v2 --no-commit

  forge install OpenZeppelin/openzeppelin-contracts --no-commit

  git submodule add https://github.com/GNSPS/solidity-bytes-utils.git lib/solidity-bytes-utils
  ```
</CodeGroup>

Then add to your `foundry.toml` under `[profile.default]`:

<CodeGroup>
  ```toml wrap forge theme={null}
  [profile.default]
  src = "src"
  out = "out"
  libs = ["lib"]

  remappings = [
      '@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/',
      '@layerzerolabs/lz-evm-protocol-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/protocol',
      '@layerzerolabs/lz-evm-messagelib-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/messagelib',
      '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/',
      'solidity-bytes-utils/=lib/solidity-bytes-utils/',
  ]

  # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
  ```
</CodeGroup>

<Info>
  LayerZero contracts work with both [**OpenZeppelin V5**](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable) and V4 contracts. Specify your desired version in your project's `package.json`:

  ```typescript wrap theme={null}
  "resolutions": {
      "@openzeppelin/contracts": "^5.0.1",
  }
  ```
</Info>

## Usage

To implement a `composer` contract, simply inherit the `IOAppComposer.sol` interface from the `oapp-evm` package:

```solidity wrap theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import { IOAppComposer } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppComposer.sol";

/**
 * @title Composer
 * @notice Demonstrates the minimum `IOAppComposer` interface necessary to receive composed messages via LayerZero.
 * @dev Implements the `lzCompose` function to process incoming composed messages.
 */
contract Composer is IOAppComposer {

    /**
     * @notice Address of the LayerZero Endpoint.
     */
    address public immutable endpoint;

    /**
     * @notice Address of the OApp that is sending the composed message.
     */
    address public immutable oApp;

    /**
     * @notice Constructs the contract and initializes state variables.
     * @dev Stores the LayerZero Endpoint and OApp addresses.
     *
     * @param _endpoint The address of the LayerZero Endpoint.
     * @param _oApp The address of the OApp that is sending composed messages.
     */
    constructor(address _endpoint, address _oApp) {
        endpoint = _endpoint;
        oApp = _oApp;
    }

    /**
     * @notice Handles incoming composed messages from LayerZero.
     * @dev Ensures the message comes from the correct OApp and is sent through the authorized endpoint.
     *
     * @param _oApp The address of the OApp that is sending the composed message.
     */
    function lzCompose(
        address _oApp,
        bytes32 /* _guid */,
        bytes calldata /* _message */,
        address /* _executor */,
        bytes calldata /* _extraData */
    ) external payable override {
        // Ensure the composed message comes from the correct OApp.
        require(_oApp == oApp, "ComposedReceiver: Invalid OApp");
        require(msg.sender == endpoint, "ComposedReceiver: Unauthorized sender");
        // ... execute logic for handling composed messages
    }
}
```

### Composed Message Execution Options

Longer `composer` messages, which contain more bytes encoded instructions, increase the cost of calling `EndpointV2.lzReceive()`.

Typically, the reason for the gas increase can be found in the additional length being added to your crosschain message, as well as the cost of invoking `EndpointV2.sendCompose()` inside your `OApp._lzReceive()` function.

Ensure that when calling `OFT.send()` and `ONFT.send()` or your own custom OApp, that you correctly estimate the cost of calling `endpoint.sendCompose()` and add the additional `LzReceiveOption` gas limit to your `SendParam.extraOptions` or OApp specific `options` argument:

```ts wrap theme={null}
// addExecutorLzReceiveOption(uint128 _gas, uint128 _value)
Options.newOptions().addExecutorLzReceiveOption(50000, 0);
```

Besides the increase cost of `EndpointV2.lzReceive()`, you should also take into account the cost of your actual `composer.lzCompose()`. Similar to lzReceive(), you can specify the `gas limit` and `msg.value` the Executor should use when calling the `composer` contract:

```ts wrap theme={null}
// addExecutorLzComposeOption(uint16 _index, uint128 _gas, uint128 _value)
Options.newOptions().addExecutorLzReceiveOption(50000, 0).addExecutorLzComposeOption(0, 30000, 0);
```

* **`_index`:** Identifies the specific composed call within a batch of composed messages. This allows for distinct execution settings for each call.

* **`_gas`:** Specifies the gas limit allocated for the composed call's execution on the destination chain. Gas requirements may vary across chains due to different opcode costs and gas mechanisms.

* **`_value`:** Determines the amount of native currency (e.g., ETH) to be sent alongside the composed call, facilitating payable functions or covering additional costs.

Review the existing documentation on [Message Execution Options](../configuration/options) to learn more.

<Warning>
  If not enough `gas limit` or `msg.value` is provided, the `EndpointV2.lzReceive()` will not execute, and will need to be manually retried either via the LayerZero Scan explorer, or manual contract call.
</Warning>

### Composing an OFT / ONFT

Both the `OFT` and `ONFT` support sending a composed message along with the crosschain token transfers.

<CodeGroup>
  ```solidity wrap OFT theme={null}
  // IOFT.sol

  /**
   * @dev Struct representing token parameters for the OFT send() operation.
   */
  struct SendParam {
      uint32 dstEid; // Destination endpoint ID.
      // highlight-next-line
      bytes32 to; // Composer address.
      uint256 amountLD; // Amount to send in local decimals.
      uint256 minAmountLD; // Minimum amount to send in local decimals.
      // highlight-next-line
      bytes extraOptions; // Compose options supplied by the caller to be used in the LayerZero message.
      // highlight-next-line
      bytes composeMsg; // The composed message for the send() operation.
      bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
  }
  ```

  ```solidity wrap ONFT theme={null}
  // IONFT.sol

  /**
   * @dev Struct representing token parameters for the ONFT send() operation.
   */
  struct SendParam {
      uint32 dstEid; // Destination LayerZero EndpointV2 ID.
      // highlight-next-line
      bytes32 to; // Composer address.
      uint256 tokenId; // The ERC721 tokenId for the send() operation.
      // highlight-next-line
      bytes extraOptions; // Compose options supplied by the caller to be used in the LayerZero message.
      // highlight-next-line
      bytes composeMsg; // The composed message for the send() operation.
      bytes onftCmd; // The ONFT command to be executed, unused in default ONFT implementations.
  }
  ```
</CodeGroup>

When calling `send()`, specify the `composer` as the to address, encode a `composeMsg` based on the composer's specification, and add a `ComposeExecutionOption` gas limit and/or msg.value depending on the composer's needs.

When creating the `composeMsg`, the OFT / ONFT will already encode specific parameters along with your message for use in the composer.

Below is how the `OFTCore` and `ONFT721Core` contracts encode the `composeMsg` and send it to the `composer`:

<CodeGroup>
  ```solidity wrap OFT theme={null}
  // OFTCore.sol

  /**
   * @dev The `OFTMsgCodec` provides a helper function to extract the `composeMsg` from
   *      the overall message. This ensures that the `composeMsg` is properly formed and can
   *      be processed by the composer.
   *
   * @notice The `composeMsg` includes both:
   *         - The `msg.sender` on the source chain (as bytes32).
   *         - The actual `composeMsg` intended for the composer.
   *
   * @notice The final encoded message structure is:
   *         abi.encodePacked(_sendTo, _amountShared, addressToBytes32(msg.sender), _composeMsg);
   */
  using OFTMsgCodec for bytes;

  /**
   * @dev When sending a message, the `composeMsg` is encoded alongside standard parameters.
   */
  (message, hasCompose) = OFTMsgCodec.encode(_sendParam.to, _toSD(_amountLD), _sendParam.composeMsg());

  /**
   * @dev If the message is composed (i.e., it contains a `composeMsg`),
   *      we extract it and send it to the composer.
   */
  if (_message.isComposed()) {
      /**
       * @dev The `composeMsg` sent to the composer includes:
       *      - `_origin.nonce` (to track the originating transaction).
       *      - `_origin.srcEid` (the source chain endpoint ID).
       *      - The actual `composeMsg` extracted from `_message`.
       */
      bytes memory composeMsg = ONFTComposeMsgCodec.encode(_origin.nonce, _origin.srcEid, _message.composeMsg());

      /**
       * @dev Sends the composed message to the specified `toAddress` (the composer).
       *
       * @notice The `composeIndex` is always `0` because batching is not implemented.
       *         - If batching is added, the index will need to be properly tracked.
       */
      endpoint.sendCompose(toAddress, _guid, 0 /* the index of composed message */, composeMsg);
  }
  ```
</CodeGroup>

Below is how the `ONFT721Core` contract encodes the `composeMsg` and sends it to the `composer`:

<CodeGroup>
  ```solidity wrap ONFT theme={null}
  // ONFT721Core.sol

  /**
   * @dev The `ONFT721MsgCodec` provides a helper function to extract the `composeMsg` from
   *      the overall message. This ensures that the `composeMsg` is properly formed and can
   *      be processed by the composer.
   *
   * @notice The `composeMsg` includes both:
   *         - The `msg.sender` on the source chain (as bytes32).
   *         - The actual `composeMsg` intended for the composer.
   *
   * @notice The final encoded message structure is:
   *         abi.encodePacked(_sendTo, _tokenId, addressToBytes32(msg.sender), _composeMsg)
   */
  using ONFT721MsgCodec for bytes;

  /**
   * @dev When sending a message, the `composeMsg` is encoded alongside standard parameters.
   */
  (message, hasCompose) = ONFT721MsgCodec.encode(_sendParam.to, _sendParam.tokenId, _sendParam.composeMsg());

  /**
   * @dev If the message is composed (i.e., it contains a `composeMsg`),
   *      we extract it and send it to the composer.
   */
  if (_message.isComposed()) {
      /**
       * @dev The `composeMsg` sent to the composer includes:
       *      - `_origin.nonce` (to track the originating transaction).
       *      - `_origin.srcEid` (the source chain endpoint ID).
       *      - The actual `composeMsg` extracted from `_message`.
       */
      bytes memory composeMsg = ONFTComposeMsgCodec.encode(_origin.nonce, _origin.srcEid, _message.composeMsg());

      /**
       * @dev Sends the composed message to the specified `toAddress` (the composer).
       *
       * @notice The `composeIndex` is always `0` because batching is not implemented.
       *         - If batching is added, the index will need to be properly tracked.
       */
      endpoint.sendCompose(toAddress, _guid, 0 /* the index of composed message */, composeMsg);
  }
  ```
</CodeGroup>

This means that in your composer application, you can decode the `msg.sender` for specific checks, along with the other composer encodings.

### Message Encoding Reference

Both OFT and ONFT use a two-step message flow when composing. This section documents the message structures using OFT as the primary example.

#### Source Chain Message

When sending a crosschain transfer with a `composeMsg`, the token contract encodes the message for transit. For OFT, this uses `OFTMsgCodec`:

```
                                 ┌──────────────────────────────────────────────────────────────────────────┐
                                 │                       OFT Message Layout                                 │
                                 ├────────────────────┬───────────────┬─────────────────────────────────────┤
                                 │       sendTo       │   amountSD    │           composeMsg                │
                                 │      32 bytes      │    8 bytes    │ [composeFrom (32)][payload (var)]   │
                                 ├────────────────────┴───────────────┴─────────────────────────────────────┤
                                 │                    32              40                                    │
                                 └──────────────────────────────────────────────────────────────────────────┘
```

| Bytes | Field        | Type      | Description                                            |
| ----- | ------------ | --------- | ------------------------------------------------------ |
| 0–31  | `sendTo`     | `bytes32` | Recipient address (composer contract if using compose) |
| 32–39 | `amountSD`   | `uint64`  | Amount in shared decimals (6 decimal precision)        |
| 40+   | `composeMsg` | `bytes`   | Optional: `[composeFrom (msg.sender)][your payload]`   |

#### Composed Message (What Your Composer Receives)

After processing the token transfer in `_lzReceive()`, the destination contract re-encodes the data and calls `endpoint.sendCompose()`. This is the message your composer receives in `lzCompose()`.

**OFT Composed Message** (`OFTComposeMsgCodec`):

```
                                 ┌─────────────────────────────────────────────────────────────────────────────┐
                                 │                     OFT Composed Message Layout                             │
                                 ├─────────┬─────────┬──────────────────┬──────────────────┬───────────────────┤
                                 │  nonce  │ srcEid  │     amountLD     │   composeFrom    │    composeMsg     │
                                 │ 8 bytes │ 4 bytes │     32 bytes     │     32 bytes     │                   │
                                 ├─────────┴─────────┴──────────────────┴──────────────────┴───────────────────┤
                                 │         8         12                 44                 76                  │
                                 └─────────────────────────────────────────────────────────────────────────────┘
```

| Bytes | Field         | Type      | Description                                                |
| ----- | ------------- | --------- | ---------------------------------------------------------- |
| 0–7   | `nonce`       | `uint64`  | Unique identifier for tracking the originating transaction |
| 8–11  | `srcEid`      | `uint32`  | Source endpoint ID (originating chain)                     |
| 12–43 | `amountLD`    | `uint256` | Amount of tokens in local decimals (full precision)        |
| 44–75 | `composeFrom` | `bytes32` | Address of the original sender on the source chain         |
| 76+   | `composeMsg`  | `bytes`   | The arbitrary payload you passed to `send()`               |

<Info>
  ### OFT: amountSD vs amountLD

  The OFT converts from shared decimals (`amountSD`, uint64) to local decimals (`amountLD`, uint256) before calling your composer. Your composer receives the **full precision** amount in the destination chain's native token decimals.
</Info>

**ONFT Composed Message** (`ONFTComposeMsgCodec`):

The ONFT uses a simpler structure without an amount field (since NFTs are unique):

| Bytes | Field         | Type      | Description                                                |
| ----- | ------------- | --------- | ---------------------------------------------------------- |
| 0–7   | `nonce`       | `uint64`  | Unique identifier for tracking the originating transaction |
| 8–11  | `srcEid`      | `uint32`  | Source endpoint ID (originating chain)                     |
| 12–43 | `composeFrom` | `bytes32` | Address of the original sender on the source chain         |
| 44+   | `composeMsg`  | `bytes`   | The arbitrary payload you passed to `send()`               |

#### Codec Functions

Import the appropriate codec in your composer contract:

```solidity wrap theme={null}
// For OFT composers
import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol";

// For ONFT composers
import { ONFTComposeMsgCodec } from "@layerzerolabs/onft-evm/contracts/libs/ONFTComposeMsgCodec.sol";
```

**OFTComposeMsgCodec Functions:**

| Function                           | Returns        | Description                                              |
| ---------------------------------- | -------------- | -------------------------------------------------------- |
| `nonce(bytes calldata _msg)`       | `uint64`       | Extracts the unique transaction identifier               |
| `srcEid(bytes calldata _msg)`      | `uint32`       | Extracts the source endpoint ID (originating chain)      |
| `amountLD(bytes calldata _msg)`    | `uint256`      | Extracts the token amount in local decimals              |
| `composeFrom(bytes calldata _msg)` | `bytes32`      | Extracts the original sender address on the source chain |
| `composeMsg(bytes calldata _msg)`  | `bytes memory` | Extracts the arbitrary payload passed to `send()`        |
| `addressToBytes32(address _addr)`  | `bytes32`      | Converts an address to bytes32 (left-padded with zeros)  |
| `bytes32ToAddress(bytes32 _b)`     | `address`      | Converts bytes32 back to an address                      |

The `ONFTComposeMsgCodec` provides the same functions except `amountLD()` (since NFTs don't have amounts).

<Tip>
  The `composeFrom` field is useful for authorization checks or refunds back to the source chain.
</Tip>

For example, see the following `composer` example which mocks an ERC20 token swap after receiving from an OFT:

```solidity wrap theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { IOAppComposer } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppComposer.sol";
import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol";

/**
 * @title SwapMock Contract
 * @notice Mocks an ERC20 token swap in response to receiving an OFT message via LayerZero.
 * @dev This contract interacts with LayerZero's Omnichain Fungible Token (OFT) Standard,
 *      processing incoming OFT messages (`lzCompose`) and executing a token swap action.
 */
contract SwapMock is IOAppComposer {
    using SafeERC20 for IERC20;

    /// @notice The ERC20 token used for swaps.
    IERC20 public erc20;

    /// @notice Address of the LayerZero Endpoint.
    address public immutable endpoint;

    /// @notice Address of the OApp that is sending the composed message.
    address public immutable oApp;

    /**
     * @notice Emitted when a token swap is executed.
     * @dev This event logs the swap details, including the recipient, token, and amount swapped.
     *
     * @param user The address of the user who receives the swapped tokens.
     * @param tokenOut The address of the ERC20 token being swapped.
     * @param amount The amount of tokens swapped.
     */
    event Swapped(address indexed user, address tokenOut, uint256 amount);

    /**
     * @notice Constructs the `SwapMock` contract.
     * @dev Initializes the contract by setting the ERC20 token, LayerZero endpoint, and OApp address.
     *
     * @param _erc20 The address of the ERC20 token that will be used in swaps.
     * @param _endpoint The LayerZero Endpoint address.
     * @param _oApp The address of the OApp that is sending the composed message.
     */
    constructor(address _erc20, address _endpoint, address _oApp) {
        erc20 = IERC20(_erc20);
        endpoint = _endpoint;
        oApp = _oApp;
    }

    /**
     * @notice Handles incoming composed messages from LayerZero and executes a token swap.
     * @dev Decodes the `composeMsg` from `_message`, extracts relevant parameters, and transfers
     *      tokens to the intended recipient.
     *
     *      The `message` is structured in the sender's contract and includes:
     *      - `_nonce`: A unique identifier for tracking the message.
     *      - `_srcEid`: The source endpoint ID, identifying the originating chain.
     *      - `_amountLD`: The amount of tokens in local decimals being transferred.
     *      - `_composeFrom`: The address of the original sender (encoded as `bytes32`).
     *      - `_composeMsg`: The payload containing the recipient address.
     *
     * @param _oApp The address of the originating OApp.
     * @param _message The encoded message containing the `composeMsg`.
     */
    function lzCompose(
        address _oApp,
        bytes32 /*_guid*/,
        bytes calldata _message,
        address /*_executor*/,
        bytes calldata /*_extraData*/
    ) external payable override {
        require(_oApp == oApp, "SwapMock: Invalid OApp");
        require(msg.sender == endpoint, "SwapMock: Unauthorized sender");

        // Decode the nonce (unique identifier for the transaction)
        uint64 _nonce = OFTComposeMsgCodec.nonce(_message);

        // Decode the source endpoint ID (originating chain)
        uint32 _srcEid = OFTComposeMsgCodec.srcEid(_message);

        // Decode the amount in local decimals being transferred
        uint256 _amountLD = OFTComposeMsgCodec.amountLD(_message);

        // Decode the `composeFrom` address (original sender) from bytes32 to address
        bytes32 _composeFromBytes = OFTComposeMsgCodec.composeFrom(_message);
        address _composeFrom = OFTComposeMsgCodec.bytes32ToAddress(_composeFromBytes);

        // Decode the actual `composeMsg` payload to extract the recipient address
        bytes memory _actualComposeMsg = OFTComposeMsgCodec.composeMsg(_message);
        address _receiver = abi.decode(_actualComposeMsg, (address));

        // Execute the token swap by transferring `_amountLD` to `_receiver`
        erc20.safeTransfer(_receiver, _amountLD);

        // Emit an event for logging the swap details
        emit Swapped(_receiver, address(erc20), _amountLD);
    }
}
```

### Composing an OApp

1. **Source OApp:** Sends a crosschain message via `_lzSend()` to a destination chain.

2. **Destination OApp:** Receives the crosschain message via `_lzReceive()` and initiates composed calls using `EndpointV2.sendCompose()`:

```solidity wrap theme={null}
/**
 * @dev Handles incoming LayerZero messages and sends a composed message using `endpoint.sendCompose()`.
 * @notice This function processes received packets and relays them to a composed receiver.
 *
 * @param _guid A globally unique identifier for tracking the packet.
 * @param payload The encoded message payload.
 */
function _lzReceive(
    Origin calldata /*_origin*/,
    bytes32 _guid,
    bytes calldata payload,
    address /*_executor*/,
    bytes calldata /*_extraData*/
) internal override {
    /**
     * @dev Decode the payload based on the expected format from the sender application.
     *      The structure of `payload` depends entirely on how the sender encoded it.
     *      In this case, we assume the sender encoded a string message and a composer address.
     *      If the sender encodes different types or a different order, this decoding must be updated accordingly.
     */
    (string memory _message, address _composedAddress) = abi.decode(payload, (string, address));

    // Store received data in the destination OApp
    data = _message;

    // Send a composed message to the composed receiver using the same GUID
    endpoint.sendCompose(_composedAddress, _guid, 0, payload);
}
```

3. **Composer:** Contracts that implement business logic to handle incoming composed messages via `EndpointV2.lzCompose()`.
