_options?
Because the source chain has no concept of the destination chain’s state, you must specify the amount of gas you anticipate will be necessary for executing your lzReceive or lzCompose method on the destination smart contract.
LayerZero provides robust Message Execution Options, which allow you to specify arbitrary logic as part of the message transaction, such as the gas amount and msg.value the Executor pays for message delivery, the order of message execution, or dropping an amount of gas to a destination address.
The most common options you will use when building are lzReceiveOption, lzComposeOption, and lzNativeDropOption.
It’s important to remember that gas values may vary depending on the destination chain. For example, all new Ethereum transactions cost
21000 wei, but other chains may have lower or higher opcode costs, or entirely different gas mechanisms.Options Builders
A Solidity library and off-chain SDK have been provided to build specific Message Options for your application.-
OptionsBuilder.sol: Can be imported from@layerzerolabs/oapp-evm. -
options.ts: Can be imported from@layerzerolabs/lz-v2-utilities.
Generating Options
You can generate options depending on your OApp’s development environment:-
Remix: for quick testing in Remix, you can deploy locally to the Remix VM a contract using the
OptionsBuilder.sollibrary. See the example provided below. Open in Remix What is Remix?
-
Foundry:
_optionscan be generated directly in your Foundry unit tests using theOptionsBuilder.sollibrary. See the OmniCounter Test file as an example for how to properly invoke options. -
Hardhat: you can also locally declare options in Hardhat via the
options.tsfile.
_options bytes array to simplify your experience when switching between environments.
Import Options
All_options tools must be imported into your environment to be used.
Options Library
Import theOptionsBuilder from @layerzerolabs/oapp-evm into either your Foundry test or smart contract to be deployed locally.
Options SDK
Start by importingOptions from @layerzerolabs/lz-v2-utilities.
Initialize Options
ThenewOptions method is used to initialize a new bytes array.
OptionsBuilder library methods, simplifying the creation and manipulation of message execution options.
Add Options Types
When generating_options, you will want to allocate specific gas amounts for handling different message types used in your smart contract. For instance, addExecutorLzReceiveOption is a method that can be used to specify how much gas limit and msg.value the Executor uses when calling lzReceive on the receiving chain.
Options methods to add more Executor message handling; all packed into a single call.
See below for all Option Types.
Pass Options in Send Call
After generating_options, you will want to test them in a send call.
Options SDK
Using the Options SDK, this can be passed directly into a Hardhat task or unit test depending on your use case.send function is being called on the YourOAppContract contract instance, passing in the destination endpoint ID, the message, and the _options that were constructed:
send function takes three parameters: _dstEid, message, and _options. The function’s logic would then use those parameters to to send a message crosschain.
Options Library
Using theOptionsBuilder.sol library, these _options can be directly referenced in your Foundry tests for quick local testing.
See
TestHelper.sol for full Foundry testing support.Option Types
There are multiple option types to take advantage of, each controlling specific handling of LayerZero messages.lzReceive Option
The lzReceive option specifies the gas values the Executor uses when calling lzReceive on the destination chain.
_gas and msg.value to be used in the lzReceive call by the Executor on the destination chain:
OPTION_TYPE_LZRECEIVE contains (uint128 _gas, uint128 _value)
_gas: The amount of gas you’d provide for the lzReceive call in source chain native tokens. 50000 should be enough for most transactions, but this value should be profiled based on your function’s specific opcode cost on each chain.
_value: The msg.value for the call. This value is often included to fund any operations that need native gas on the destination chain, including sending another nested message.
lzCompose Option
This option allows you to allocate some gas and value to your Composed Message on the destination chain. lzCompose is used when you want to call external contracts from your lzReceive function.
OPTION_TYPE_LZCOMPOSE contains (uint16 _index, uint128 _gas, uint128 _value)
_index: The index of the lzCompose() function call. When multiples of this option are added, they are summed PER index by the Executor on the remote chain. This can be useful for defining multiple composed message steps that happen sequentially.
_gas: The gas amount for the lzCompose call varies based on the destination’s compose logic and the destination chain’s characteristics (e.g., opcode pricing). It’s important to perform tailored testing to determine the optimal gas requirement for your specific transaction needs.
_value: The msg.value for the call.
lzNativeDrop Option
This option contains how much native gas you want to drop to the _receiver, this is often done to allow users or a contract to have some gas on a new chain.
OPTION_TYPE_LZNATIVEDROP contains (uint128 _amount, bytes32 _receiver)
_amount: The amount of gas in wei to drop for the receiver.
_receiver: The bytes32 representation of the receiver address.
OrderedExecution Option
By adding this option, the Executor will utilize Ordered Message Delivery. This overrides the default behavior of Unordered Message Delivery.
2 transaction fails, all subsequent transactions with this option will not be executed until the previous message has been resolved with.
bytes: The argument should always be initialized as an empty bytes array ("").
Duplicate Option Types
Multiple options of the same type can be passed and appended into the same options array. The logic on how multiple options of the same type are summed differs per option type:-
lzReceive: Both the_gasand_valueparameters are summed. -
lzCompose: Both the_gasand_valueparameters are summed by index. -
lzNativeDrop: The_amountparameter is summed by unique_receiveraddress.
Determining Gas Costs
Gas profiling and optimization is outside the scope of LayerZero’s documentation, however, the following resources may be useful for determining what_options should be used for your _lzReceive and lzCompose calls.
Tenderly
For supported chains, the Tenderly Gas Profiler can be extremely useful for determining how much to reduce your execution options by:
45,358 wei for gas.
-
Provided
lzReceiveOption:50000wei -
Actual
lzReceiveCost:45,358wei
_options based on the message types for your application.