Cross-Chain Messaging with LayerZero V2
LayerZero V2 is an omnichain messaging protocol with per-application DVN committees and a pay-as-you-go executor model. The Tenzro MCP server exposes 20 LayerZero tools — quote/send/track for raw messages, OFT for omnichain ERC-20s, Stargate V2 for native ETH/USDC/USDT, and the unified Value Transfer API covering 130+ chains including Solana. This tutorial walks the full lifecycle.
What You'll Build
- Quote and send an arbitrary cross-chain message
- Bridge an OFT (omnichain fungible token)
- Bridge native USDC via Stargate V2
- Use the unified Value Transfer API across 130+ chains
Architecture
LayerZero V2 on Tenzro — component map
src chain verification dst chain
--------- ------------ ---------
App.send() ──► EndpointV2 ──► DVN_1 ──┐
DVN_2 ──┼──► ULN ──► EndpointV2 ──► App.lzReceive()
DVN_N ──┘ ▲
│
Executor pays gas
DVNs = Decentralized Verifier Networks (per-app configurable committees)
ULN = Ultra-Light Node verification on dst
GUID = globally unique message ID for trackingStep 1: Encode Executor Options
V2 options are a TLV-encoded bytes blob that tells the executor how much gas and value to forward to lzReceive on the destination:
// EXECUTOR_LZ_RECEIVE option — pays the executor to call lzReceive on the dst chain
lz_encode_options({
"option_type": 3,
"executor_gas": 200000,
"executor_value": "0"
})
-> { options: "0x00030100110100000000000000000000000000030d40" }Step 2: Quote the Fee
This is a real EndpointV2.quote() eth_call — no estimates, no caching. The returned native_fee is what your wallet must attach as msg.value:
lz_quote_fee({
"src_chain": "ethereum",
"dst_chain": "base",
"receiver": "0xReceiverOnBase...",
"payload": "0xdeadbeef",
"options": "0x00030100110100000000000000000000000000030d40"
})
-> {
native_fee: "0.0005", // ETH
lz_token_fee: "0",
endpoint: "0x1a44..."
}Step 3: Send an Arbitrary Message
lz_send_message({
"src_chain": "ethereum",
"dst_chain": "base",
"receiver": "0xReceiverOnBase...",
"payload": "0xdeadbeef",
"options": "0x00030100110100000000000000000000000000030d40",
"refund_address": "0xSender..."
})
-> {
tx_calldata: "0x...", // EndpointV2.send() encoded
to: "0x1a44...", // EndpointV2 address
value: "500000000000000", // native_fee in wei
guid_hint: "0x..."
}Step 4: Track Delivery
lz_track_message({ "guid": "0x..." })
-> {
status: "Delivered", // Inflight | Delivered | Failed
src_tx: "0x...",
dst_tx: "0x...",
dvn_count: 2
}Step 5: Bridge an OFT
amount is uint64 amountSD(shared decimals) — the OFT contract handles the local-decimal conversion. The helper auto-quotes the fee and returns ready-to-sign calldata:
// 1. Quote an OFT send
lz_oft_quote({
"oft_address": "0xOftOnEthereum...",
"dst_chain": "base",
"receiver": "0xReceiverOnBase...",
"amount": "1000000000" // uint64 amountSD (shared decimals)
})
-> { native_fee: "0.0004", lz_token_fee: "0" }
// 2. Build the OFT send calldata (with auto fee quoting)
lz_oft_send({
"oft_address": "0xOftOnEthereum...",
"dst_chain": "base",
"receiver": "0xReceiverOnBase...",
"amount": "1000000000"
})
-> {
tx_calldata: "0x...", // OFT.send() encoded
to: "0xOftOnEthereum...",
value: "400000000000000"
}Step 6: Native Bridging via Stargate V2
For native ETH/USDC/USDT where you'd otherwise need a wrapper, Stargate V2 routes through liquidity pools — returning the full sequence including ERC-20 approval:
// Stargate V2 native token bridge (ETH, USDC, USDT) — uses StargatePoolNative
lz_stargate_quote({
"token": "USDC",
"src_chain": "ethereum",
"dst_chain": "base",
"amount": "100.0",
"receiver": "0xReceiverOnBase..."
})
-> {
amount_received: "99.98",
native_fee: "0.0012",
min_amount_out: "99.88"
}
lz_stargate_send({
"token": "USDC",
"src_chain": "ethereum",
"dst_chain": "base",
"amount": "100.0",
"receiver": "0xReceiverOnBase..."
})
-> {
steps: [
{ kind: "erc20_approve", token: "0xA0b86...", spender: "0xStargate...", amount: "100000000" },
{ kind: "call", to: "0xStargate...", data: "0x...", value: "1200000000000000" }
]
}Step 7: Unified Value Transfer API
The highest-level path — abstract over LayerZero's V2 endpoints, OFT deployments, and Stargate pools with a single quote → build → status flow:
// Unified Value Transfer API — covers 130+ chains including Solana
lz_transfer_quote({
"src_chain": "ethereum",
"dst_chain": "solana",
"token": "USDC",
"amount": "25.0",
"receiver": "SolanaPublicKey..."
})
-> {
quote_id: "vq_01HR...",
amount_out: "24.97",
native_fee: "0.0008"
}
lz_transfer_build({ "quote_id": "vq_01HR..." })
-> { steps: [ /* signable tx steps for the wallet */ ] }
lz_transfer_status({ "quote_id": "vq_01HR..." })
-> { status: "delivered", src_tx: "0x...", dst_tx: "5Kq..." }Step 8: CLI
# List all LayerZero supported chains (130+)
tenzro layerzero list-chains
# Quote a message send
tenzro layerzero quote-fee \
--src ethereum --dst base \
--receiver 0x... --payload 0xdeadbeef
# Send arbitrary cross-chain message
tenzro layerzero send \
--src ethereum --dst base \
--receiver 0x... --payload 0xdeadbeef
# Bridge TNZO via the omnichain OFT
tenzro bridge transfer \
--adapter layerzero \
--from ethereum --to base \
--token TNZO --amount 100.0 \
--recipient 0x...
# Track delivery
tenzro layerzero track --guid 0x...Step 9: TypeScript SDK
import { TenzroClient } from "@tenzro/sdk";
const client = new TenzroClient();
// 1. Quote the fee on-chain via EndpointV2.quote()
const q = await client.layerzero.quoteFee({
src_chain: "ethereum",
dst_chain: "base",
receiver: "0xReceiverOnBase...",
payload: "0xdeadbeef",
options: "0x00030100110100000000000000000000000000030d40",
});
console.log("native fee (ETH):", q.native_fee);
// 2. Build send calldata
const send = await client.layerzero.send({
src_chain: "ethereum",
dst_chain: "base",
receiver: "0xReceiverOnBase...",
payload: "0xdeadbeef",
options: "0x00030100110100000000000000000000000000030d40",
});
// 3. Submit via your wallet
const hash = await wallet.sendTransaction({
to: send.to,
data: send.tx_calldata,
value: BigInt(send.value),
});
// 4. Track by GUID
const status = await client.layerzero.trackMessage({ guid: send.guid_hint! });LayerZero vs CCT vs Wormhole. LayerZero V2 is per-app configurable: each application chooses its own DVN committee and security threshold. CCIP uses a global DON + RMN. Wormhole uses a fixed 19-Guardian set. For independent trust domains on high-value flows, run two adapters in parallel and require both to attest before releasing value.
What You Learned
- EndpointV2 quote/send — real on-chain fee quoting, GUID-based tracking
- Options TLV — encoding executor gas and value for
lzReceive - OFT — omnichain fungible token model with
amountSD - Stargate V2 + Value Transfer API — native-token flows across 130+ chains
Next Steps
- See the CCIP Messaging tutorial for Chainlink's alternative
- See the LI.FI Aggregation tutorial to auto-route across LayerZero/CCIP/Wormhole
- Read the LayerZero reference docs