DVN and Executor Configuration
This guide explains how to configure Decentralized Verifier Networks (DVNs), Executors, and message libraries for your Sui 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-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
Configure Receive DVN (Inbound)
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,
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});
Configure Send DVN (Outbound)
// 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→ UseOAppUlnConfigBcs.serialize()for DVN configoapp_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
- OApp Overview - Base messaging standard
- OFT Overview - Token standard and deployment
- OFT SDK - Complete SDK methods and examples
- Technical Overview - Sui fundamentals and architecture
- Protocol Overview - Complete message workflows
- Troubleshooting - Common configuration issues