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 Sui OApp or OFT using the SDK.
Production deployments should use multiple required DVNs from independent operators. A single-DVN configuration means a compromise of that one verifier results in unrestricted forged messages on the pathway. The examples on this page show the LayerZero Labs DVN with a <SECONDARY_PROVIDER> placeholder so the snippet does not silently model a single-DVN production setup. Replace <SECONDARY_PROVIDER> with a real non-LayerZero-Labs provider — see DVN Addresses for available providers per chain. See the Integration Checklist for production DVN guidance.
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-sui-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 Sui 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-sui-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,
// Replace <SECONDARY_PROVIDER> with a non-LayerZero-Labs DVN; see /v2/deployments/dvn-addresses
required_dvns: [
PACKAGE_DVN_LAYERZERO_ADDRESS[Stage.MAINNET],
PACKAGE_DVN_<SECONDARY_PROVIDER>_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 (MIST) | 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 Sui destinations:
const optionsForSui = Options.newOptions()
.addExecutorLzReceiveOption(5000, 0) // 5k gas units, no msg.value
.toBytes();
Note: Based on gas profiling, Sui lz_receive uses 2,000-5,000 gas units. No msg.value needed - Sui 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