Message Execution Options
When sending cross-chain messages, the source chain has no knowledge of the destination chain's state or the resources required to execute a transaction on it. Message Execution Options provide a standardized way to specify the execution requirements for transactions on the destination chain.
You can think of options
as serialized requests in bytes
that inform the off-chain infrastructure (DVNs
and Executors
) how to handle the execution of your message on the destination chain.
See Message Options for more details on why Options exist in the LayerZero protocol.
Options Builders
LayerZero provides tools to build specific Message Execution Options for your application:
EVM
OptionsBuilder.sol
: Can be imported from@layerzerolabs/oapp-evm
options.ts
: Can be imported from@layerzerolabs/lz-v2-utilities
Aptos & Solana
options.ts
: Can be imported from@layerzerolabs/lz-v2-utilities
Generating Options
EVM (Solidity)
using OptionsBuilder for bytes;
bytes memory options = OptionsBuilder.newOptions()
.addExecutorLzReceiveOption(50000, 0)
.toBytes();
All Chains (TypeScript)
import {Options} from '@layerzerolabs/lz-v2-utilities';
const options = Options.newOptions().addExecutorLzReceiveOption(gas_limit, msg_value).toBytes();
Option Types
lzReceive
Option
Specifies the gas values the Executor uses when calling lzReceive
on the destination chain.
Options.newOptions().addExecutorLzReceiveOption(gas_limit, msg_value);
lzRead
Option
Specifies the gas values and response data size the Executor uses when delivering lzRead responses.
Since the return data size is not known to the Executor ahead of time, you must estimate the expected response data size. This size is priced into the Executor's fee formula. Failure to correctly estimate the return data size will result in the Executor not delivering the response.
Options.newOptions().addExecutorLzReadOption(gas_limit, return_data_size, msg_value);
Parameters:
gas_limit
: The amount of gas for delivering the lzRead responsereturn_data_size
: The estimated size (in bytes) of the response data from the read operationmsg_value
: Themsg.value
for the call
lzCompose
Option
Allocates gas and value for Composed Messages on the destination chain.
Options.newOptions().addExecutorLzComposeOption(index, gas_limit, msg_value);
Parameters:
_index
: The index of thelzCompose()
function call_gas
: The gas amount for the lzCompose call_value
: Themsg.value
for the call
lzNativeDrop
Option
Specifies how much native gas to drop to any address on the destination chain.
Options.newOptions().addExecutorNativeDropOption(amount, receiverAddressInBytes32);
Parameters:
_amount
: The amount of gas in wei/lamports to drop_receiver
: Thebytes32
representation of the receiver address
OrderedExecution
Option
Enables ordered message delivery, overriding the default unordered delivery.
Options.newOptions().addExecutorOrderedExecutionOption('');
Chain-Specific Considerations
EVM Chains
- Gas values are specified in wei
- Gas costs vary by chain and opcode pricing
Aptos
- Gas units are similar to EVM but may have different costs
- Recommended starting gas limit: 1,500 units for
lzReceive
- Uses APT as native token
Solana
- Uses compute units instead of gas
- Requires minimum 0.0015 SOL (1,500,000 lamports) for account initialization
- Native token drops are in lamports
- Programs pull SOL from sender's account rather than pushing with transaction
Determining Gas Costs
Tenderly
For supported chains, the Tenderly Gas Profiler can help determine optimal gas values:
- Deploy and test your contract
- Use Tenderly to profile actual gas usage
- Set your options slightly above the profiled amount
Testing
Always test your gas settings thoroughly:
- Start with conservative estimates
- Profile actual usage
- Adjust based on real-world performance
- Consider chain-specific gas mechanisms
Best Practices
- Gas Profiling: Always profile your contract's gas usage on each target chain
- Conservative Estimates: Start with higher gas limits and adjust down
- Chain-Specific Testing: Test thoroughly on each target chain
- Native Caps: Check Executor's native cap for each pathway
- Multiple Options: Consider combining options for complex scenarios