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

# Configuring LayerZero Contracts

> Configure LayerZero OApp contracts with peers, enforced options, send/receive libraries, and DVN settings using layerzero.config.ts. Build omnichain applicat...

For each contract in your config file, you can configure the following:

```solidity wrap theme={null}
FromOApp.transferOwnership(newOwner)
FromOApp.setPeer(dstEid, peer)
FromOApp.setEnforcedOptions()
EndpointV2.setSendLibrary(OApp, dstEid, newLib)
EndpointV2.setReceiveLibrary(OApp, dstEid, newLib, gracePeriod)
EndpointV2.setReceiveLibraryTimeout(OApp, dstEid, lib, gracePeriod)
EndpointV2.setConfig(OApp, sendLibrary, sendConfig)
EndpointV2.setConfig(OApp, receiveLibrary, receiveConfig)
EndpointV2.setDelegate(delegate)
```

## Adding Configurations

To configure your OApp, you will need to change your `layerzero.config.ts` for your desired pathways.

<Warning>
  **Production deployments should use multiple required DVNs from independent operators.** The examples below use `<SECONDARY_DVN>` as a placeholder so the config does not silently resolve to a single-DVN configuration. Replace it with a real DVN name from [DVN Addresses](/v2/deployments/dvn-addresses) before wiring. See the [Integration Checklist](/v2/tools/integration-checklist#set-security-and-executor-configurations-on-every-pathway) for production DVN guidance.
</Warning>

LayerZero's CLI makes use of the `@layerzerolabs/metadata-tools` package, which allows for a human readable `layerzero.config.ts` file.

Here's how to use it:

1. Install metadata-tools: `pnpm add -D @layerzerolabs/metadata-tools`

2. Create a new [LZ config](/v2/concepts/glossary#lz-config) file named `layerzero.config.ts` (or edit your existing one) in the project root and use the examples below as a starting point:

   <Tabs>
     <Tab title="Example: EVM chains only">
       ```typescript wrap theme={null}
       import {ExecutorOptionType} from '@layerzerolabs/lz-v2-utilities';
       import {OAppEnforcedOption, OmniPointHardhat} from '@layerzerolabs/toolbox-hardhat';
       import {EndpointId} from '@layerzerolabs/lz-definitions';
       import {generateConnectionsConfig} from '@layerzerolabs/metadata-tools';

       const avalancheContract: OmniPointHardhat = {
         eid: EndpointId.AVALANCHE_V2_TESTNET,
         contractName: 'MyOFT',
       };

       const polygonContract: OmniPointHardhat = {
         eid: EndpointId.AMOY_V2_TESTNET,
         contractName: 'MyOFT',
       };

       const EVM_ENFORCED_OPTIONS: OAppEnforcedOption[] = [
         {
           msgType: 1,
           optionType: ExecutorOptionType.LZ_RECEIVE,
           gas: 80000,
           value: 0,
         },
         {
           msgType: 2,
           optionType: ExecutorOptionType.LZ_RECEIVE,
           gas: 80000,
           value: 0,
         },
         {
           msgType: 2,
           optionType: ExecutorOptionType.COMPOSE,
           index: 0,
           gas: 80000,
           value: 0,
         },
       ];

       export default async function () {
         // note: pathways declared here are automatically bidirectional
         // if you declare A,B there's no need to declare B,A
         const connections = await generateConnectionsConfig([
           [
             avalancheContract, // Chain A contract
             polygonContract, // Chain B contract
             [['LayerZero Labs', '<SECONDARY_DVN>'], []], // [ requiredDVN[], [ optionalDVN[], threshold ] ]
             [1, 1], // [A to B confirmations, B to A confirmations]
             [EVM_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Chain B enforcedOptions, Chain A enforcedOptions
           ],
         ]);

         return {
           contracts: [{contract: avalancheContract}, {contract: polygonContract}],
           connections,
         };
       }
       ```
     </Tab>

     <Tab title="Example: EVM chain and Solana">
       ```typescript wrap theme={null}
       import {ExecutorOptionType} from '@layerzerolabs/lz-v2-utilities';
       import {OAppEnforcedOption, OmniPointHardhat} from '@layerzerolabs/toolbox-hardhat';
       import {EndpointId} from '@layerzerolabs/lz-definitions';
       import {generateConnectionsConfig} from '@layerzerolabs/metadata-tools';

       export const avalancheContract: OmniPointHardhat = {
         eid: EndpointId.AVALANCHE_V2_TESTNET,
         contractName: 'MyOFT',
       };

       export const solanaContract: OmniPointHardhat = {
         eid: EndpointId.SOLANA_V2_TESTNET,
         address: 'HBTWw2VKNLuDBjg9e5dArxo5axJRX8csCEBcCo3CFdAy', // your OFT Store address
       };

       const EVM_ENFORCED_OPTIONS: OAppEnforcedOption[] = [
         {
           msgType: 1,
           optionType: ExecutorOptionType.LZ_RECEIVE,
           gas: 80000,
           value: 0,
         },
         {
           msgType: 2,
           optionType: ExecutorOptionType.LZ_RECEIVE,
           gas: 80000,
           value: 0,
         },
         {
           msgType: 2,
           optionType: ExecutorOptionType.COMPOSE,
           index: 0,
           gas: 80000,
           value: 0,
         },
       ];

       const SOLANA_ENFORCED_OPTIONS: OAppEnforcedOption[] = [
         {
           msgType: 1,
           optionType: ExecutorOptionType.LZ_RECEIVE,
           gas: 200000,
           value: 2500000,
         },
         {
           msgType: 2,
           optionType: ExecutorOptionType.LZ_RECEIVE,
           gas: 200000,
           value: 2500000,
         },
         {
           // Solana options use (gas == compute units, value == lamports)
           msgType: 2,
           optionType: ExecutorOptionType.COMPOSE,
           index: 0,
           gas: 0,
           value: 0,
         },
       ];

       export default async function () {
         // note: pathways declared here are automatically bidirectional
         // if you declare A,B there's no need to declare B,A
         const connections = await generateConnectionsConfig([
           [
             avalancheContract, // Chain A contract
             solanaContract, // Chain B contract
             [['LayerZero Labs', '<SECONDARY_DVN>'], []], // [ requiredDVN[], [ optionalDVN[], threshold ] ]
             [1, 1], // [A to B confirmations, B to A confirmations]
             [SOLANA_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Chain B enforcedOptions, Chain A enforcedOptions
           ],
         ]);

         return {
           contracts: [{contract: avalancheContract}, {contract: solanaContract}],
           connections,
         };
       }
       ```

       2b. If your pathways include Solana, run the Solana init config command:

       ```
       npx hardhat lz:oft:solana:init-config --oapp-config layerzero.config.ts
       ```

       * Note that only the Solana contract object requires `address` to be specified. Do not specify `address` for non-Solana contract objects.
     </Tab>
   </Tabs>

   * The above examples contains a minimal mesh with only one pathway (two chains) for demonstration purposes. You are able to add as many pathways as you need into the `connections` param, via `generateConnectionsConfig`.

3. Run the wire command:

```bash wrap theme={null}
npx hardhat lz:oapp:wire --oapp-config layerzero.config.ts
```

The wire command will process all the transactions required to connect the pathways specified in the config file. If you change anything in the config, run the command again.

Each pathway contains a `config`, containing multiple configuration structs for changing how your OApp sends and receives messages, specifically for the chain your OApp is sending `from`:

| Name                          | Type    | Description                                                                                                                                                                                                                                                         |
| ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sendLibrary`                 | Address | The message library used for configuring all sent messages `from` this chain. (e.g., `SendUln302.sol`)                                                                                                                                                              |
| `receiveLibraryConfig`        | Struct  | A struct containing the receive message library address (e.g., `ReceiveUln302.sol`), and an optional BigInt, `gracePeriod`, the time to wait before updating to a new MessageLib version during version migration. Controls how the `from` chain receives messages. |
| `receiveLibraryTimeoutConfig` | Struct  | An optional param, defining when the old receive library (`lib`) will expire (`expiry`) during version migration.                                                                                                                                                   |
| `sendConfig`                  | Struct  | Controls how the OApp sends `from` this pathway, containing two more structs: `executorConfig` and `ulnConfig` (DVNs).                                                                                                                                              |
| `receiveConfig`               | Struct  | Controls how the OApp (`from`) receives messages, specifically the `ulnConfig` (DVNs).                                                                                                                                                                              |
| `enforcedOptions`             | Struct  | Controls the minimum destination gas sent to the destination, per message type (e.g., `_lzReceive`, `lzCompose`, etc.) in your OApp.                                                                                                                                |

<Tip>
  When adding a `config`, consider that connections moves in a bidirectional, two-way path:

  * The `sendConfig` applies to all message sent `from` **Chain A** and received by the `to` address, **Chain B**.

  * The `receiveConfig` applies to all messages received by **Chain A** (`from`), sent from **Chain B** (the `to` contract).

  For example, this `config: {}` applies only to how the `bscContract` sends messages to the `sepoliaContract`, and how the `bscContract` receives messages from the `sepoliaContract`.
</Tip>

### Adding `sendLibrary`

Every configuration should start by adding a `sendLibrary`.

```typescript wrap theme={null}
connections: [
    {
      // Sets the peer `from -> to`. Optional, you do not have to connect all pathways.
      from: bscContract,
      to: sepoliaContract,
      // Optional Configuration
      config: {
        // Required Send Library Address on BSC
        // highlight-next-line
        sendLibrary: "0x0000000000000000000000000000000000000000",
      },
    },
],
```

When running `lz:oapp:wire`, this will call `EndpointV2`:

```solidity wrap theme={null}
// LayerZero/V2/protocol/contracts/interfaces/IMessageLibManager.sol

function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
```

Each [MessageLib](/v2/concepts/protocol/message-send-library) contains the available configuration options for the protocol, and so must be set by the application owner to prevent unintended updates.

<Info>
  You should use the `sendLibrary` address for the chain you're sending `from` (i.e., `SendUln302.sol` on BSC).
</Info>

<Info>
  The MessageLib Registry is append only, meaning that old Message Libraries will always be available for OApps. Locking your Library is only necessary to prevent updates.
</Info>

### Adding `receiveLibrary`

Every configuration should also add a `receiveLibrary`. Similar to the `sendLibrary`, the OApp owner must also set the Receive Library to ensure that your configured application settings will be locked.

To do this, add a `receiveLibraryConfig`:

```typescript wrap theme={null}
connections: [
    {
      // Sets the peer `from -> to`. Optional, you do not have to connect all pathways.
      from: bscContract,
      to: sepoliaContract,
      // Optional Configuration
      config: {
        // Required Send Library Address on BSC
        sendLibrary: "0x0000000000000000000000000000000000000000",
        // highlight-start
        receiveLibraryConfig: {
          // Required Receive Library Address on BSC
          receiveLibrary: "0x0000000000000000000000000000000000000000",
          // Optional Grace Period for Switching Receive Library Address on BSC
          gracePeriod: BigInt(0),
        },
        // Optional Receive Library Timeout for when the Old Receive Library Address will no longer be valid on BSC
        receiveLibraryTimeoutConfig: {
          lib: "0x0000000000000000000000000000000000000000",
          expiry: BigInt(0),
        },
        // highlight-end
      },
    },
],
```

The Receive Library also provides two additional parameters to help future-proof OApp's for migrating MessageLib versions:

* `gracePeriod`: the time to wait before updating to a new MessageLib version during version migration. If the grace period is 0, it will delete the timeout configuration.

* `expiry`: the time at which messages in-flight from the old library will be considered invalid. This is mainly for handling messages that are in-flight during the migration.

In most cases, setting the `gracePeriod` to 0 will be sufficient.

When running `lz:oapp:wire`, this config will call `EndpointV2`:

```solidity wrap theme={null}
// LayerZero/V2/protocol/contracts/interfaces/IMessageLibManager.sol

function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;

function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _gracePeriod) external;
```

### Adding `sendConfig`

Your `sendConfig` controls what [DVN addresses](../../deployments/dvn-addresses) and [Executor addresses](../../deployments/deployed-contracts) should be paid to verify and execute when a message is sent.

<Info>
  Each DVN and Executor contains both onchain and off-chain component. When sending a message, you pay the DVNs and Executors contracts on the source chain, and they relay the message to the equivalent contracts on the destination chain.

  For your `sendConfig`, use the DVNs and Executor contract addresses on the same chain as your sending OApp.
</Info>

<Tip>
  DVNs only need to be the same for a given pathway.

  You can have one set of DVNs verifying transactions from `Arbitrum` to `Base` and `Base` to `Arbitrum`, and a separate set of DVNs verifying transactions from `Arbitrum` to `Avalanche` and `Avalanche` to `Arbitrum`.
</Tip>

```typescript wrap theme={null}
connections: [
    {
      // Sets the peer `from -> to`. Optional, you do not have to connect all pathways.
      from: bscContract,
      to: sepoliaContract,
      // Optional Configuration
      config: {
        // Required Send Library Address on BSC
        sendLibrary: "0x0000000000000000000000000000000000000000",
        // Required Receive Library Config
        receiveLibraryConfig: {
          // Required Receive Library Address on BSC
          receiveLibrary: "0x0000000000000000000000000000000000000000",
          // Optional Grace Period for Switching Receive Library Address on BSC
          gracePeriod: BigInt(0),
        },
        // Optional Receive Library Timeout for when the Old Receive Library Address will no longer be valid on BSC
        receiveLibraryTimeoutConfig: {
          lib: "0x0000000000000000000000000000000000000000",
          expiry: BigInt(0),
        },
        // highlight-start
        // Optional Send Configuration
        // @dev Controls how the `from` chain sends messages to the `to` chain.
        sendConfig: {
          executorConfig: {
            maxMessageSize: 10000,
            // The configured Executor address on BSC
            executor: "0x0000000000000000000000000000000000000000",
          },
          ulnConfig: {
            // The number of block confirmations to wait on BSC before emitting the message from the source chain (BSC).
            confirmations: BigInt(0),
            // The address of the DVNs you will pay to verify a sent message on the source chain (BSC).
            // The destination tx will wait until ALL `requiredDVNs` verify the message.
            requiredDVNs: [],
            // The address of the DVNs you will pay to verify a sent message on the source chain (BSC).
            // The destination tx will wait until the configured threshold of `optionalDVNs` verify a message.
            optionalDVNs: [
              "0x_POLYHEDRA_DVN_ADDRESS_ON_BSC",
              "0x_LAYERZERO_DVN_ADDRESS_ON_BSC",
            ],
            // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified.
            optionalDVNThreshold: 2,
          },
        },
        // highlight-end
      },
    },
],
```

This will call `EndpointV2.setConfig`:

```solidity wrap theme={null}
// LayerZero/V2/protocol/contracts/interfaces/IMessageLibManager.sol

struct SetConfigParam {
    uint32 eid;
    uint32 configType;
    bytes config;
}

function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
```

The Executor and ULN `configType` and `config`:

```solidity wrap theme={null}
// LayerZero/V2/messagelib/contracts/uln/uln302/SendUln302.sol

uint32 internal constant CONFIG_TYPE_EXECUTOR = 1;
uint32 internal constant CONFIG_TYPE_ULN = 2;
```

```solidity wrap theme={null}
// LayerZero/V2/messagelib/contracts/uln/SendLibBase.sol

struct ExecutorConfig {
    uint32 maxMessageSize;
    address executor;
}
```

```solidity wrap theme={null}
// LayerZero/V2/messagelib/contracts/uln/UlnBase.sol

struct UlnConfig {
    uint64 confirmations;
    // we store the length of required DVNs and optional DVNs instead of using DVN.length directly to save gas
    uint8 requiredDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
    uint8 optionalDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
    uint8 optionalDVNThreshold; // (0, optionalDVNCount]
    address[] requiredDVNs; // no duplicates. sorted an an ascending order. allowed overlap with optionalDVNs
    address[] optionalDVNs; // no duplicates. sorted an an ascending order. allowed overlap with requiredDVNs
}
```

### Adding `receiveConfig`

The receive configuration controls what [DVN addresses](../../deployments/dvn-addresses) your OApp expects to have verified the message in-flight.

<Tip>
  For example, if `BSC` is receiving messages from `Sepolia`, you should use the DVN contract addresses on `BSC` for each DVN provider you have in your `sendConfig`.
</Tip>

```typescript wrap theme={null}
connections: [
    {
      // Sets the peer `from -> to`. Optional, you do not have to connect all pathways.
      from: bscContract,
      to: sepoliaContract,
      // Optional Configuration
      config: {
        // Required Send Library Address on BSC
        sendLibrary: "0x0000000000000000000000000000000000000000",
        // Required Receive Library Config
        receiveLibraryConfig: {
          // Required Receive Library Address on BSC
          receiveLibrary: "0x0000000000000000000000000000000000000000",
          // Optional Grace Period for Switching Receive Library Address on BSC
          gracePeriod: BigInt(0),
        },
        // Optional Receive Library Timeout for when the Old Receive Library Address will no longer be valid on BSC
        receiveLibraryTimeoutConfig: {
          lib: "0x0000000000000000000000000000000000000000",
          expiry: BigInt(0),
        },
        // Optional Send Configuration
        // @dev Controls how the `from` chain sends messages to the `to` chain.
        sendConfig: {
          executorConfig: {
            maxMessageSize: 99,
            // The configured Executor address on BSC
            executor: "0x0000000000000000000000000000000000000000",
          },
          ulnConfig: {
            // The number of block confirmations to wait on BSC before emitting the message from the source chain (BSC).
            confirmations: BigInt(42),
            // The address of the DVNs you will pay to verify a sent message on the source chain (BSC).
            // The destination tx will wait until ALL `requiredDVNs` verify the message.
            requiredDVNs: [],
            // The address of the DVNs you will pay to verify a sent message on the source chain (BSC).
            // The destination tx will wait until the configured threshold of `optionalDVNs` verify a message.
            optionalDVNs: [
              "0x_POLYHEDRA_DVN_ADDRESS_ON_BSC",
              "0x_LAYERZERO_DVN_ADDRESS_ON_BSC",
            ],
            // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified.
            optionalDVNThreshold: 2,
          },
        },
        // highlight-start
        // Optional Receive Configuration
        // @dev Controls how the `from` chain receives messages from the `to` chain.
        receiveConfig: {
          ulnConfig: {
            // The number of block confirmations to expect from the `to` chain (Sepolia).
            confirmations: BigInt(42),
            // The address of the DVNs your `receiveConfig` expects to receive verifications from on the `from` chain (BSC).
            // The `from` chain's OApp will wait until the configured threshold of `requiredDVNs` verify the message.
            requiredDVNs: [],
            // The address of the `optionalDVNs` you expect to receive verifications from on the `from` chain (BSC).
            // The destination tx will wait until the configured threshold of `optionalDVNs` verify the message.
            optionalDVNs: [
              "0x_POLYHEDRA_DVN_ADDRESS_ON_BSC",
              "0x_LAYERZERO_DVN_ADDRESS_ON_BSC",
            ],
            // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified.
            optionalDVNThreshold: 2,
          },
        },
        // highlight-end
      },
    },
],
```

This will set the `receiveConfig` in `EndpointV2.setConfig`:

```solidity wrap theme={null}
// LayerZero/V2/messagelib/contracts/uln/UlnBase.sol

struct UlnConfig {
    uint64 confirmations;
    // we store the length of required DVNs and optional DVNs instead of using DVN.length directly to save gas
    uint8 requiredDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
    uint8 optionalDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
    uint8 optionalDVNThreshold; // (0, optionalDVNCount]
    address[] requiredDVNs; // no duplicates. sorted an an ascending order. allowed overlap with optionalDVNs
    address[] optionalDVNs; // no duplicates. sorted an an ascending order. allowed overlap with requiredDVNs
}
```

### Adding `enforcedOptions`

You can specify both a minimum destination gas and `msg.value` that users must pay for both your contract's `lzReceive` and \`\`lzCompose\` logic to execute as intended.

The CLI Toolkit enables you to configure your message options in a human-readable format, provided that your OApp has added an Enforced Options.

<Info>
  The **Omnichain Fungible Token (OFT) Standard** by default already has **Enforced Options** added to the contract, with two message types available:

  ```solidity wrap theme={null}
  // @dev execution types to handle different enforcedOptions
  uint16 internal constant SEND = 1; // a standard token transfer via lzReceive
  uint16 internal constant SEND_AND_CALL = 2; // a token transfer, followed by a composable call via lzCompose
  ```
</Info>

```typescript wrap theme={null}
connections: [
    {
      // Sets the peer `from -> to`. Optional, you do not have to connect all pathways.
      from: bscContract,
      to: sepoliaContract,
      // Optional Configuration
      config: {
        // Required Send Library Address on BSC
        sendLibrary: "0x0000000000000000000000000000000000000000",
        receiveLibraryConfig: {
          // Required Receive Library Address on BSC
          receiveLibrary: "0x0000000000000000000000000000000000000000",
          // Optional Grace Period for Switching Receive Library Address on BSC
          gracePeriod: BigInt(0),
        },
        // Optional Receive Library Timeout for when the Old Receive Library Address will no longer be valid on BSC
        receiveLibraryTimeoutConfig: {
          lib: "0x0000000000000000000000000000000000000000",
          expiry: BigInt(0),
        },
        // Optional Send Configuration
        // @dev Controls how the `from` chain sends messages to the `to` chain.
        sendConfig: {
          executorConfig: {
            maxMessageSize: 99,
            // The configured Executor address on BSC
            executor: "0x0000000000000000000000000000000000000000",
          },
          ulnConfig: {
            // The number of block confirmations to wait on BSC before emitting the message from the source chain (BSC).
            confirmations: BigInt(42),
            // The address of the DVNs you will pay to verify a sent message on the source chain (BSC).
            // The destination tx will wait until ALL `requiredDVNs` verify the message.
            requiredDVNs: [],
            // The address of the DVNs you will pay to verify a sent message on the source chain (BSC).
            // The destination tx will wait until the configured threshold of `optionalDVNs` verify a message.
            optionalDVNs: [
              "0x_POLYHEDRA_DVN_ADDRESS_ON_BSC",
              "0x_LAYERZERO_DVN_ADDRESS_ON_BSC",
            ],
            // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified.
            optionalDVNThreshold: 2,
          },
        },
        // Optional Receive Configuration
        // @dev Controls how the `from` chain receives messages from the `to` chain.
        receiveConfig: {
          ulnConfig: {
            // The number of block confirmations to expect from the `to` chain (Sepolia).
            confirmations: BigInt(42),
            // The address of the DVNs your `receiveConfig` expects to receive verifications from on the `from` chain (BSC).
            // The `from` chain's OApp will wait until the configured threshold of `requiredDVNs` verify the message.
            requiredDVNs: [],
            // The address of the `optionalDVNs` you expect to receive verifications from on the `from` chain (BSC).
            // The destination tx will wait until the configured threshold of `optionalDVNs` verify the message.
            optionalDVNs: [
              "0x_POLYHEDRA_DVN_ADDRESS_ON_BSC",
              "0x_LAYERZERO_DVN_ADDRESS_ON_BSC",
            ],
            // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified.
            optionalDVNThreshold: 2,
          },
        },
        // highlight-start
        // Optional Enforced Options Configuration
        // @dev Controls how much gas to use on the `to` chain, which the user pays for on the source `from` chain.
        enforcedOptions: [
          {
            msgType: 1, // depending on OAppOptionType3
            optionType: ExecutorOptionType.LZ_RECEIVE,
            gas: 65000, // gas limit in wei for EndpointV2.lzReceive
            value: 0, // msg.value in wei for EndpointV2.lzReceive
          },
          {
            msgType: 1,
            optionType: ExecutorOptionType.NATIVE_DROP,
            amount: 0, // amount of native gas token in wei to drop to receiver address
            receiver: "0x0000000000000000000000000000000000000000",
          },
          {
            msgType: 2,
            optionType: ExecutorOptionType.LZ_RECEIVE,
            index: 0,
            gas: 65000, // gas limit in wei for EndpointV2.lzReceive
            value: 0, // msg.value in wei for EndpointV2.lzReceive
          },
          {
            msgType: 2,
            optionType: ExecutorOptionType.COMPOSE,
            index: 0, // index of EndpointV2.lzCompose message
            gas: 50000, // gas limit in wei for EndpointV2.lzCompose
            value: 0, // msg.value in wei for EndpointV2.lzCompose
          },
        ],
        // highlight-end
      },
    },
],
```

This will call `OApp.setEnforcedOptions` assuming your OApp has inherited from `OAppOptionsType3.sol`:

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

struct EnforcedOptionParam {
  uint32 eid; // Endpoint ID
  uint16 msgType; // Message Type
  bytes options; // Additional options
}

function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;
```

### Adding `delegate`

```typescript wrap theme={null}
// layerzero.config.ts

contracts: [
  {
    contract: sepolia,
    config: {
      delegate: '0x0000000000000000000000000000000000000000',
    },
  },
  {
    contract: bsc,
    config: {
      delegate: '0x0000000000000000000000000000000000000000',
    },
  },
];
```

### Adding `owner`

```typescript wrap theme={null}
// layerzero.config.ts

contracts: [
  {
    contract: sepolia,
    config: {
      owner: '0x0000000000000000000000000000000000000000',
    },
  },
  {
    contract: bsc,
    config: {
      owner: '0x0000000000000000000000000000000000000000',
    },
  },
];
```

To transfer ownership, you will need to run a separate command:

```bash wrap theme={null}
npx hardhat lz:ownable:transfer-ownership --oapp-config layerzero.config.ts
```

<Warning>
  Once you transfer ownership, you can no longer call `OApp.setDelegate` and `OApp.setEnforcedOptions`. You should ensure all other configurations have been set to your liking before transferring ownership.
</Warning>

## Applying Changes

Wiring your contracts will set the `peer` address for your OApp or OFT and initialize the desired configuration in your `layerzero.config.ts`.

### Wiring Contracts

The CLI Tool makes this one step easier by enabling you to wire and configure your contract pathways with a single command:

```bash wrap theme={null}
$ npx hardhat lz:oapp:wire --oapp-config layerzero.config.ts
```

Before wiring your contracts, you should review your `layerzero.config.ts` to ensure that you have specified accurately the configuration you want to set.

Wiring your contracts will set the `peer` address for your OApp or OFT and initialize the desired configuration in your `layerzero.config.ts`.

The CLI Tool makes this one step easier by enabling you to wire and configure your contract pathways with a single command:

```bash wrap theme={null}
$ npx hardhat lz:oapp:wire --oapp-config layerzero.config.ts
```

Before wiring your contracts, you should review your `layerzero.config.ts` to ensure that you have specified accurately the configuration you want to set.

### Checking `setPeers`

To check if your contracts have correctly been set to communicate with one another, you can run:

```bash wrap theme={null}
npx hardhat lz:oapp:peers:get --oapp-config layerzero.config.ts
```

### Checking Pathway `config`

To confirm your OApp's configuration has been set as intended, you can run:

```bash wrap theme={null}
$ npx hardhat lz:oapp:config:get --oapp-config layerzero.config.ts
```

### Checking `executor`

To see your OApp's configured executor, you can run:

```bash wrap theme={null}
npx hardhat lz:oapp:config:get:executor
```

### Checking `enforcedOptions`

To see your OApp's configured execution gas has been set as intended, you can run:

```bash wrap theme={null}
npx hardhat lz:oapp:enforced-opts:get --oapp-config layerzero.config.ts
```

### Checking Pathway `defaults`

To see what the default configuration is for any pathway, run:

```bash wrap theme={null}
npx hardhat lz:oapp:config:get --oapp-config layerzero.config.ts
```

### Wiring via Safe multisig

If your contracts are owned by a Safe multisig wallet, you must define the multisig's `safeUrl` and `safeAddress` per chain in your `hardhat.config.ts` file to enable the submission of wire transactions for multisig approval. `safeUrl` refers to the URL of the [Safe Transaction Service](https://docs.safe.global/core-api/api-safe-transaction-service) for a given network. For the endpoints deployed by Safe themselves on popular networks, you can find the URLs in the [Safe Transaction Service API Reference](https://docs.safe.global/core-api/transaction-service-reference/mainnet).

#### Step 1: Configure your Safe multisig

In your hardhat config, add `safeConfig` to your networks, with your network specific `safeUrl` and `safeAddress` mapped accordingly:

```javascript wrap theme={null}
// hardhat.config.ts

networks: {
  // Include configurations for other networks as needed
  fuji: {
    /* ... */
    // Network-specific settings
    safeConfig: {
      safeUrl: 'http://something', // URL of the Safe Transaction Service for the network
      safeAddress: 'address' // Address of the Safe wallet for the network
    }
  }
}
```

#### Step 2: Use your safe config

When wiring, pass the `--safe` flag in your wire command.

```bash wrap theme={null}
$ npx hardhat lz:oapp:wire --oapp-config layerzero.config.ts --safe
```

This command initiates the wiring process under the multisig setup, pushing transactions to the specified multisig wallet for necessary approvals.

<Note>
  Ensure your development tools are up to date to utilize this feature, as it relies on the latest versions of the required dependencies.
</Note>
