Cross-Chain Messaging Development (LayerZero, Wormhole, Axelar)
Interoperability is one of the unsolved problems in the blockchain industry. Ethereum doesn't know what's happening on Solana. Arbitrum is isolated from Cosmos. Each cross-chain bridge is a separate point of trust, and history shows this is expensive: Wormhole ($325M hack, 2022), Ronin ($625M, 2022), Nomad ($190M, 2022). Despite this, cross-chain messaging is fundamental infrastructure for any multi-chain product. Let's examine three major protocols and how to work with them correctly.
LayerZero: Omnichain Messaging
LayerZero v2 is the most popular protocol for EVM-to-EVM and EVM-to-non-EVM messaging. Architecture based on division of responsibility: DVN (Decentralized Verifier Networks) verify messages, Executors execute them on destination chain.
How the Protocol Works
A message in LayerZero travels:
Source Chain: OApp.send() → EndpointV2.send() → emit PacketSent event
↓
DVNs monitor event
DVNs verify on destination
↓
Destination Chain: EndpointV2 receives verifications → Executor calls lzReceive()
DVN is verifiers that read source chain and attest packet correctness on destination chain. Can configure Required DVN + Optional DVN + threshold. Example: require 2 of 3 (LayerZero Labs DVN + Google Cloud DVN + Polyhedra DVN).
Writing OApp (Omnichain Application)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { OApp, Origin, MessagingFee } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
import { OptionsBuilder } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OptionsBuilder.sol";
contract CrossChainMessenger is OApp {
using OptionsBuilder for bytes;
event MessageReceived(uint32 srcEid, bytes32 sender, string message);
constructor(address _endpoint, address _owner)
OApp(_endpoint, _owner) {}
// Send message to another network
function sendMessage(
uint32 dstEid, // destination endpoint ID (e.g., 30101 = Ethereum)
string calldata message,
bytes calldata options // gas limit for destination execution
) external payable {
bytes memory payload = abi.encode(message);
// Options: how much gas to allocate on destination chain
bytes memory lzOptions = OptionsBuilder.newOptions()
.addExecutorLzReceiveOption(200_000, 0); // 200k gas, 0 msg.value
MessagingFee memory fee = _quote(dstEid, payload, lzOptions, false);
require(msg.value >= fee.nativeFee, "Insufficient fee");
_lzSend(
dstEid,
payload,
lzOptions,
MessagingFee(msg.value, 0),
payable(msg.sender)
);
}
// Receive message on destination chain
function _lzReceive(
Origin calldata origin,
bytes32 guid,
bytes calldata payload,
address executor,
bytes calldata extraData
) internal override {
string memory message = abi.decode(payload, (string));
emit MessageReceived(origin.srcEid, origin.sender, message);
// Your business logic here
}
// Estimate cost before sending
function quoteSend(
uint32 dstEid,
string calldata message
) external view returns (uint256 nativeFee) {
bytes memory payload = abi.encode(message);
bytes memory options = OptionsBuilder.newOptions()
.addExecutorLzReceiveOption(200_000, 0);
MessagingFee memory fee = _quote(dstEid, payload, options, false);
return fee.nativeFee;
}
}
OFT: Omnichain Fungible Token
Most popular LayerZero use case is cross-chain token. OFT standard allows token to exist natively on multiple networks without wrapped versions:
import { OFT } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFT.sol";
contract MyOFTToken is OFT {
constructor(
string memory name,
string memory symbol,
address lzEndpoint,
address owner
) OFT(name, symbol, lzEndpoint, owner) {}
}
On source chain tokens are burned, on destination chain minted. Total supply remains constant. No wrapped tokens, no liquidity pools for bridging.
Wormhole: Universal Messaging
Wormhole v2 supports 30+ networks including Ethereum, Solana, Cosmos chains, Aptos, Sui — broader coverage than LayerZero. Architecture: Guardian network of 19 nodes (PoA) publishes VAA (Verifiable Action Approval) — signed confirmation of event on source chain.
Core VAA Mechanics
import {
getSignedVAAWithRetry,
parseSequenceFromLogEth,
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
import { ethers } from "ethers";
// 1. Publish message on ETH side
const coreBridge = new ethers.Contract(WORMHOLE_ETH_BRIDGE, BRIDGE_ABI, signer);
const tx = await coreBridge.publishMessage(
0, // nonce
payload, // bytes data
1 // consistency level (finality requirement)
);
// 2. Get sequence from receipt
const receipt = await tx.wait();
const sequence = parseSequenceFromLogEth(receipt, WORMHOLE_ETH_BRIDGE);
// 3. Wait for VAA from Guardians (usually 15-60 seconds)
const { vaaBytes } = await getSignedVAAWithRetry(
["https://wormhole-v2-mainnet-api.certus.one"],
CHAIN_ID_ETH,
emitterAddress,
sequence,
{ retryTimeout: 1000, retryAttempts: 60 }
);
// 4. Redeem on destination chain (e.g., Solana)
// vaaBytes contains signed VAA, pass to
// program on Solana for verification and execution
Wormhole vs LayerZero: Choosing
| Criteria | LayerZero v2 | Wormhole v2 |
|---|---|---|
| Supported networks | ~50 (EVM-focused) | 30+ (including non-EVM) |
| Security model | Configurable DVN | Guardian PoA (19 nodes) |
| Token standard | OFT (no liquidity) | NTT / xERC20 |
| Solana support | Yes | Yes (native) |
| Developer tooling | Excellent | Good |
| Latency | ~2–5 min (EVM→EVM) | ~15–60 sec |
For EVM-to-EVM — LayerZero v2. If project has Solana, Aptos, or Cosmos — look at Wormhole.
Axelar: General Message Passing
Axelar is Cosmos-based blockchain acting as routing layer between networks. GMP (General Message Passing) is protocol for arbitrary smart contract calls on destination chain.
import { AxelarExecutable } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";
import { IAxelarGateway } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol";
import { IAxelarGasService } from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol";
contract CrossChainNFTBridge is AxelarExecutable {
IAxelarGasService immutable gasService;
constructor(address gateway, address _gasService)
AxelarExecutable(gateway) {
gasService = IAxelarGasService(_gasService);
}
function bridgeNFT(
string calldata destChain, // "Polygon", "Avalanche", etc.
string calldata destContract, // contract address on destination
uint256 tokenId
) external payable {
bytes memory payload = abi.encode(msg.sender, tokenId);
// Pay gas for execution on destination chain
gasService.payNativeGasForContractCall{value: msg.value}(
address(this), destChain, destContract, payload, msg.sender
);
gateway.callContract(destChain, destContract, payload);
_burnNFT(msg.sender, tokenId);
}
// Called by Axelar relayer on destination chain
function _execute(
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload
) internal override {
(address recipient, uint256 tokenId) = abi.decode(payload, (address, uint256));
_mintNFT(recipient, tokenId);
}
}
Axelar especially strong for Cosmos↔EVM bridging — native IBC support allows routing through entire Cosmos ecosystem.
Cross-Chain Protocol Security
Cross-chain messaging is high-risk. Several mandatory practices:
Rate limiting — limit volume of funds transferred per period. If protocol compromised, limits potential damage.
mapping(uint256 => uint256) public dailyVolume; // day → volume
function _checkRateLimit(uint256 amount) internal {
uint256 today = block.timestamp / 1 days;
dailyVolume[today] += amount;
require(dailyVolume[today] <= DAILY_LIMIT, "Daily limit exceeded");
}
Pause mechanism — emergency pause on both sides of bridge. Controlled by multisig with short timelock (or without — for emergency).
Trusted path validation — on destination chain verify message came from your contract on source chain, not arbitrary caller.
Avoid storing large amounts in bridge contracts — liquidity pool model (lock-and-mint) creates huge honeypot. Mint-and-burn (like OFT) is safer.
When to Choose Which Protocol
LayerZero — building omnichain token (OFT), omnichain NFT, or EVM-to-EVM data messaging with flexible security configuration.
Wormhole — need Solana, Aptos, Sui, or maximum network coverage.
Axelar — architecture has Cosmos chains, or need high-level GMP abstracted from specific networks.
For production systems with real funds: security audit cross-chain logic is mandatory, team with bridge security experience (Zellic, OtterSec, Trail of Bits).







