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.
This guide explains how to configure Decentralized Verifier Networks (DVNs), Executors, and message libraries for your IOTA OApp or OFT using the SDK.
Overview
Configuration is done through the OApp SDK instance. All examples use sdk.getOApp(packageId) to get the OApp instance, then call SDK methods for configuration.
Configuration flow:
- Set message libraries (optional)
- Configure DVNs for send/receive (optional but recommended)
- Set enforced options (optional)
- Set peer addresses (required - opens pathway, call last!)
SDK Setup
import {SDK} from '@layerzerolabs/lz-iotal1-sdk-v2';
import {Stage} from '@layerzerolabs/lz-definitions';
const sdk = new SDK({client, stage: Stage.MAINNET});
const oapp = sdk.getOApp(yourPackageId); // Always use SDK factory
Configuration Methods
Set Peer
// Configure peer for destination chain
await oapp.setPeerMoveCall(tx, dstEid, peerBytes32);
Address format: Use package ID for IOTA peers, 32-byte address for other chains.
Set Message Libraries
// Set custom send library
await oapp.setSendLibraryMoveCall(tx, dstEid, libraryAddress);
// Set custom receive library
await oapp.setReceiveLibraryMoveCall(tx, srcEid, libraryAddress, gracePeriod);
Default: Uses Endpoint defaults if not configured.
DVN Configuration
import {
SDK,
OAppUlnConfigBcs,
PACKAGE_ULN_302_ADDRESS,
OBJECT_ULN_302_ADDRESS,
PACKAGE_DVN_LAYERZERO_ADDRESS,
} from '@layerzerolabs/lz-iotal1-sdk-v2';
import {Stage} from '@layerzerolabs/lz-definitions';
const sdk = new SDK({client, stage: Stage.MAINNET});
const oapp = sdk.getOApp(yourPackageId);
// Encode configuration
const config = OAppUlnConfigBcs.serialize({
use_default_confirmations: false,
use_default_required_dvns: false,
use_default_optional_dvns: true,
uln_config: {
confirmations: 15,
required_dvns: [PACKAGE_DVN_LAYERZERO_ADDRESS[Stage.MAINNET]],
optional_dvns: [],
optional_dvn_threshold: 0,
},
}).toBytes();
// Two-step Call pattern
const tx = new Transaction();
const configCall = await oapp.setConfigMoveCall(
tx,
PACKAGE_ULN_302_ADDRESS[Stage.MAINNET],
30184, // Remote EID
3, // CONFIG_TYPE_RECEIVE_ULN
config,
);
tx.moveCall({
target: `${PACKAGE_ULN_302_ADDRESS[Stage.MAINNET]}::uln_302::set_config`,
arguments: [tx.object(OBJECT_ULN_302_ADDRESS[Stage.MAINNET]), configCall],
});
await client.signAndExecuteTransaction({transaction: tx, signer: keypair});
// Same pattern, use CONFIG_TYPE_SEND_ULN = 2
const configCall = await oapp.setConfigMoveCall(
tx,
PACKAGE_ULN_302_ADDRESS[Stage.MAINNET],
30184,
2, // CONFIG_TYPE_SEND_ULN
config,
);
Config types: 1 = Executor, 2 = Send ULN, 3 = Receive ULN
DVN addresses: Use PACKAGE_DVN_LAYERZERO_ADDRESS[Stage.MAINNET] or see Deployed Contracts.
Set Enforced Options
import {Options} from '@layerzerolabs/lz-v2-utilities';
const options = Options.newOptions()
.addExecutorLzReceiveOption(60000, 0) // Gas for destination
.toBytes();
await oapp.setEnforcedOptionsMoveCall(tx, dstEid, msgType, options);
Gas Limit Recommendations
Based on gas profiling:
OApp/OFT Operations
| Operation | Gas Used (IOTA) | Recommended Budget | Notes |
|---|
lz_receive | 2,000-4,172 | 3,500-5,000 | For OApps and custom business logic, this needs independent profiling |
oft_send | 4,728,620 | 6,700,000 | Includes endpoint + ULN |
dvn_verify | 5,684,108 | 7,700,000 | Verification submission |
dvn_commit | 517,248 | 2,500,000 | Commit verification |
Enforced Options Examples
For EVM destinations:
import {Options} from '@layerzerolabs/lz-v2-utilities';
// Standard OApp message
const options = Options.newOptions()
.addExecutorLzReceiveOption(60000, 0) // 60k gas, no msg.value
.toBytes();
// OFT with compose
const optionsCompose = Options.newOptions()
.addExecutorLzReceiveOption(200000, 0) // Higher for compose
.toBytes();
For IOTA destinations:
const optionsForIOTA = Options.newOptions()
.addExecutorLzReceiveOption(5000, 0) // 5k gas units, no msg.value
.toBytes();
Note: Based on gas profiling, IOTA lz_receive uses 2,000-5,000 gas units. No msg.value needed - IOTA handles storage internally.
Common Issues
Errors:
InvalidBCSBytes → Use OAppUlnConfigBcs.serialize() for DVN config
oapp_registry::get_messaging_channel abort code: 1 → Used object ID instead of package ID as peer
- Channel not initialized → Registration creates MessagingChannel automatically (no manual init needed)
Next Steps