What You Will Build
By the end of this guide, you should have:- an Executor fee contract deployed on each source chain you want to support;
- an OApp configured to pay your Executor instead of the default Executor;
- an off-chain process watching source-chain
PacketSentevents; - fee validation that proves your Executor was paid for the exact packet;
- destination-chain verification and execution logic; and
- a repeatable smoke test that sends a message in both directions.
Executor Lifecycle
A complete Executor has two off-chain workflows: commit and execute.- Watch the source chain for
PacketSentonEndpointV2. - Validate that the same transaction paid your Executor through the trusted send library.
- Decode the packet and route it by destination endpoint ID.
- Wait for destination-chain DVN verification.
- Call
ReceiveUln302.commitVerification(packetHeader, payloadHash)when the packet is verifiable. - Poll
EndpointV2View.executable(origin, receiver)until the packet is executable. - Call
EndpointV2.lzReceive(origin, receiver, guid, message, extraData). - Persist the result so restarts do not duplicate or lose work.
Prerequisites
Install:- Node.js
>=18 - npm, pnpm, or Yarn
- Foundry
gitjq- RPC URLs for both chains
- a funded signer on both chains
Choose Your Chains
Use the deployment pages or metadata API to collect the current Endpoint, SendUln302, ReceiveUln302, EndpointV2View, and endpoint ID values for each chain.Bootstrap A Reference Implementation
The Paladin reference implementation is a useful EVM starting point because it already separates the source-chain event provider, destination-chain executor, packet codec, and sample contracts.contracts/lib/forge-std, add the missing submodule entry and retry:
The reference is intentionally minimal. Before operating it beyond a testnet
smoke test, add durable state, replay checkpoints, rate limiting, metrics, and
classified retry handling.
Deploy The On-Chain Executor
Deploy an Executor fee contract on every source chain that should pay your Executor. The contract must implementILayerZeroExecutor:
_options.
After deploying your Executor contracts, configure your OApp’s send library so messages to the remote EID use your Executor:
Configure OApp Peers
Your test OApps must be deployed on both chains and configured as peers before_lzSend can route packets.
For EVM-to-EVM tests, set each remote peer as a left-padded bytes32 value:
setPeer, confirm that both the send library and receive library configs are written for each pathway before quoting a message.
Create Executor Configuration
Your off-chain Executor needs one config entry per chain. Use current metadata for LayerZero protocol addresses and your own deployed Executor contract forexecutor.
bootstrapLookbackBlocks only if your worker persists checkpoints and can rate-limit historical replay. Endpoint-wide log scans can be noisy because the Endpoint emits traffic for all applications and executors.
Validate Executor Payment
Do not decide that your Executor was paid by checking whether a transaction contains anyExecutorFeePaid event. A single transaction can send multiple packets, and only one of them may have paid your Executor.
Use this receipt-walking algorithm for each PacketSent log:
- Fetch the transaction receipt.
- Start from the specific
PacketSent.logIndex. - Walk backward through logs in that transaction.
- Stop if you hit another
PacketSent; that earlier packet owns earlier fee events. - Ignore any event not emitted by the configured
trustedSendLib. - Decode
ExecutorFeePaid(address executor, uint256 fee). - Accept the packet only if
executor == yourExecutorAddress.
- multiple
PacketSentlogs in one transaction; ExecutorFeePaidemitted by an untrusted address;ExecutorFeePaidfor another Executor;- no
ExecutorFeePaid; and - zero or insufficient fee.
Commit Verification
After accepting a packet, derive:origin:{ srcEid, sender, nonce }receiver: destination OApp addresspacketHeaderpayloadHash
Execute The Message
After commit, query:
When executable:
Run The Off-Chain Worker
For the reference implementation, run:- load each configured chain;
- register one provider per source EID;
- register one executor per destination EID; and
- begin watching
PacketSent.
eth_getLogs range:
Smoke Test Both Directions
Before treating the Executor as usable, run a two-way smoke test.- Quote a message from chain A to chain B.
- Send a small payload from chain A to chain B.
- Confirm the source transaction emitted
PacketSentandExecutorFeePaid. - Confirm your worker committed verification on chain B.
- Confirm your worker executed
lzReceiveon chain B. - Query the destination OApp and confirm the payload was stored or handled.
- Repeat from chain B to chain A.
Production Runtime Checklist
A production Executor needs more than the minimal reference loop.Persistent State
Persist one row per packet:
On restart, load pending packets from storage before scanning new blocks.
Checkpointed Event Ingestion
Do not rely onwatchEvent alone. RPC subscriptions can miss logs during disconnects or process restarts.
Use:
- per-chain block checkpoints;
- bounded
eth_getLogswindows; - packet GUID and tx/log-index deduplication;
- rate limits per RPC provider;
- retries with exponential backoff; and
- alerts when the scanner falls behind.
Retry Classification
Classify errors before retrying:Signer Operations
Monitor:- native token balance on every destination chain;
- account nonce and stuck transactions;
- RPC latency and error rate;
- gas price spikes;
- oldest pending packet age; and
- queue depth by destination EID.
Observability
Expose health and metrics:- last source block scanned;
- last packet observed;
- packets committed;
- packets executed;
- retries by class;
- failures by class;
- signer balances;
- pending queue depth; and
- RPC provider status.
Troubleshooting
Non-EVM Chains
The EVM packet codec assumes sender and receiver values can be converted frombytes32 to EVM addresses. For Solana, Aptos, Sui, or other non-EVM chains, implement chain-specific versions of:
- event provider;
- packet/address codec;
- destination execution client;
- transaction construction;
- signer management; and
- gas or fee accounting.
Summary
The minimal reference Executor is enough to prove the LayerZero V2 flow on testnet. To make it independently operable, you need:- current metadata-derived chain config;
- an Executor contract deployed on each source chain;
- OApp send config pointing to your Executor;
- strict per-packet fee validation;
- current receive-library and endpoint verifiability checks;
- a two-way smoke test; and
- durable runtime state, checkpointed replay, retries, and monitoring before production.
Important Security And Operational Considerations
Can a custom Executor forge or alter LayerZero messages?
No. A custom Executor cannot make the destination Endpoint accept an unverified packet. Message authenticity is still enforced by the configured MessageLib and DVNs. The primary risk is not message forgery. The primary risk is that a poorly implemented Executor can fail to deliver valid messages, execute messages without being properly paid, overpay for gas, or expose its signer infrastructure.What happens if I validate ExecutorFeePaid incorrectly?
You may execute packets that did not pay your Executor.
This most commonly happens when an implementation checks whether the transaction contains any ExecutorFeePaid event instead of tying the payment to the exact PacketSent log. Batched sends can emit multiple PacketSent events in one transaction, and each packet must be matched to its own fee event.
Mitigation:
- walk receipt logs backward from the exact
PacketSent.logIndex; - require the event emitter to equal the trusted send library;
- require the decoded
executorto equal your Executor contract; and - stop when a previous
PacketSentis reached.
What happens if I use the wrong Endpoint, SendUln, ReceiveUln, or DVN address?
Messages can become stuck or fail in non-obvious ways. A packet may be observed on the source chain, but never become committable or executable on the destination chain because the worker is checking the wrong contract or the OApp pathway is configured differently than the worker expects. Mitigation:- source addresses from the LayerZero metadata endpoint or deployment pages;
- store the exact metadata snapshot used for a deployment;
- validate every configured address at startup; and
- include a two-way smoke test after every config change.
What happens if my worker misses events?
Valid packets can remain undelivered. RPC subscriptions and HTTP polling can miss logs during restarts, disconnects, rate limits, or provider outages. Mitigation:- persist per-chain scan checkpoints;
- replay bounded block ranges on startup;
- deduplicate by packet GUID and tx/log index;
- alert when the scanner falls behind; and
- use an indexed or production-grade RPC provider for production traffic.
What happens if I do not persist packet state?
The worker can lose pending packets during a restart, retry the same packet indefinitely, or submit duplicate commit and execution transactions. Mitigation:- persist packet status transitions;
- treat packet GUID as the primary idempotency key;
- record commit and execution transaction hashes; and
- resume pending work from storage before processing new logs.
What are the signer risks?
The Executor signer needs native gas on every destination chain it executes on. If the signer is overfunded, reused as an owner key, or stored on an insecure host, compromise of the Executor process can become a key-management incident. Mitigation:- use a dedicated low-privilege Executor signer;
- keep owner, treasury, and Executor keys separate;
- monitor signer balances and nonce state;
- rotate keys through an operational runbook; and
- fund only the amount required for expected execution volume plus buffer.
Can a bad gas or fee model lose money?
Yes. If yourgetFee and assignJob logic undercharges relative to destination gas costs, your Executor can pay more to execute packets than it collected on the source chain. If it over-trusts user-provided options, it can also accept work that is too expensive or impossible to execute.
Mitigation:
- parse message options before quoting;
- cap supported gas and value options;
- price destination native gas and token conversion conservatively;
- include an operator margin; and
- pause or reprice pathways when gas markets move sharply.
Is a stalled Executor a security issue?
It can be. A stalled Executor does not let invalid messages through, but it can create liveness failures for applications that rely on automatic delivery. If a chain is running its own Executor because another off-chain service was turned off, the Executor becomes part of that chain’s application availability path. Mitigation:- alert on oldest pending packet age;
- alert on failed commit or execution attempts;
- maintain enough destination gas;
- document manual execution fallback; and
- run a tested recovery process for RPC outages and stuck nonces.