Configuring Contracts
For each contract in your config file, you can configure the following:
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.
Initializing Config
You can initialize your OApp configurations by running:
npx hardhat lz:oapp:config:init --contract-name DEPLOYMENT_NAME --oapp-config CONFIG_FILE_NAME
This will auto-populate the provided config file, or create the file if the path does not exist, with the current LayerZero default configurations as a placeholder.
For example, running:
npx hardhat lz:oapp:config:init --contract-name MyOApp --oapp-config testnet.layerzero.config.ts
will create a new file in my directory with the correct type interface:
contracts/
deploy/
test/
foundry.toml
hardhat.config.ts
layerzero.config.ts
testnet.layerzero.config.ts <-----
Make sure the DEPLOYMENT_NAME
exists in your ./deployments
folder, otherwise the task will fail.
Head to the layerzero.config.ts
and scroll down to module.exports
.
As explained previously, the CLI Toolkit organizes your configurations on a per-pathway basis:
module.exports = {
// Define the contracts to be deployed on each network
// Each contract is associated with a specific blockchain.
contracts: [
{
contract: sepoliaContract,
},
{
contract: bscContract,
},
],
// Define the pathway between each contract.
// This allows for cross-chain communication using LayerZero.
connections: [
{
from: bscContract,
to: sepoliaContract,
},
{
from: sepoliaContract,
to: bscContract,
},
],
};
To add a specific pathway configuration, add a config: {}
to your connection:
module.exports = {
// Define the contracts to be deployed on each network
// Each contract is associated with a specific blockchain.
contracts: [
{
contract: sepoliaContract,
},
{
contract: bscContract,
},
],
// Define the pathway between each contract.
// This allows for cross-chain communication using LayerZero.
connections: [
{
from: bscContract,
to: sepoliaContract,
config: {},
},
{
from: sepoliaContract,
to: bscContract,
},
],
};
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. |
When adding a config
, consider that connections moves in a bidirectional, two-way path:
The
sendConfig
applies to all message sentfrom
Chain A and received by theto
address, Chain B.The
receiveConfig
applies to all messages received by Chain A (from
), sent from Chain B (theto
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
.
Adding sendLibrary
Every configuration should start by adding a sendLibrary
.
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",
},
},
],
When running lz:oapp:wire
, this will call EndpointV2
:
// LayerZero/V2/protocol/contracts/interfaces/IMessageLibManager.sol
function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
Each MessageLib contains the available configuration options for the protocol, and so must be set by the application owner to prevent unintended updates.
You should use the sendLibrary
address for the chain you're sending from
(i.e., SendUln302.sol
on BSC).
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.
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
:
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),
},
},
},
],
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
:
// 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 and Executor addresses should be paid to verify and execute when a message is sent.
Each DVN and Executor contains both on-chain 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.
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
.
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: 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,
},
},
},
},
],
This will call EndpointV2.setConfig
:
// 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
:
// LayerZero/V2/messagelib/contracts/uln/uln302/SendUln302.sol
uint32 internal constant CONFIG_TYPE_EXECUTOR = 1;
uint32 internal constant CONFIG_TYPE_ULN = 2;
// LayerZero/V2/messagelib/contracts/uln/SendLibBase.sol
struct ExecutorConfig {
uint32 maxMessageSize;
address executor;
}
// 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 your OApp expects to have verified the message in-flight.
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
.
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,
},
},
// 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,
},
},
},
},
],
This will set the receiveConfig
in EndpointV2.setConfig
:
// 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 Option Message Type.
The Omnichain Fungible Token (OFT) Standard by default already has Enforced Options added to the contract, with two message types available:
// @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
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,
},
},
// 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
},
],
},
},
],
This will call OApp.setEnforcedOptions
assuming your OApp has inherited from OAppOptionsType3.sol
:
// 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;
Review the Transaction Pricing section and the Execution Options to better understand how you should add your execution gas settings.
Adding delegate
// layerzero.config.ts
contracts: [
{
contract: sepolia,
config: {
delegate: '0x0000000000000000000000000000000000000000',
},
},
{
contract: bsc,
config: {
delegate: '0x0000000000000000000000000000000000000000',
},
},
];
Adding owner
// 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:
npx hardhat lz:ownable:transfer-ownership --oapp-config layerzero.config.ts
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.
Final Config
Your final config may have different settings, but should define the following parameters:
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: 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,
},
},
// 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(0),
// 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,
},
},
// 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,
optionType: ExecutorOptionType.LZ_RECEIVE,
gas: 60000,
value: 0,
},
{
msgType: 1,
optionType: ExecutorOptionType.NATIVE_DROP,
amount: 0,
receiver: "0x0000000000000000000000000000000000000000",
},
{
msgType: 2,
optionType: ExecutorOptionType.LZ_RECEIVE,
index: 0,
gas: 60000,
value: 1,
},
{
msgType: 2,
optionType: ExecutorOptionType.COMPOSE,
index: 0,
gas: 50000,
value: 0,
},
],
},
},
],
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
. If unfamiliar with this concept, review the OApp Quickstart.
Wiring Contracts
The CLI Tool makes this one step easier by enabling you to wire and configure your contract pathways with a single command:
$ 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
. If unfamiliar with this concept, review the OApp Quickstart.
The CLI Tool makes this one step easier by enabling you to wire and configure your contract pathways with a single command:
$ 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:
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:
$ npx hardhat lz:oapp:config:get --oapp-config layerzero.config.ts
Checking executor
To see your OApp's configured executor, you can run:
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:
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:
npx hardhat lz:oapp:config:get --oapp-config layerzero.config.ts
Wiring via Gnosis Safe
If your contracts are owned by a gnosis 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.
Step 1: Configure your gnosis
In your hardhat config, add safeConfig
to your networks, with your specific safeUrl
and safeAddress
mapped accordingly:
// hardhat.config.ts
networks: {
// Include configurations for other networks as needed
fuji: {
/* ... */
// Network-specific settings
safeConfig: {
safeUrl: 'http://something',
safeAddress: 'address'
}
}
}
Step 2: Use your safe config
When wiring, pass the --safe
flag in your wire command.
$ 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.
Ensure your development tools are up to date to utilize this feature, as it relies on the latest versions of the required dependencies.