How Does Cross-Chain Message Passing Actually Work? A Walkthrough for Developers

Cross-chain · 2026-05-30 · 比特三棱镜编辑部
Ask AI

I have a friend who is a Web3 developer. He has shipped DeFi contracts and written Solana programs, but when he picked up a cross-chain project in early 2026 he spent the entire first week unable to picture how a cross-chain message actually moves. That confusion is common: people use bridges and worry about seconds and dollars but never draw the diagram. If you are about to write an OApp (omnichain application) or a GMP-based system, you have to draw it.

This post is for him, and for everyone in his shoes.

GMP cross-chain message flow overview

One-line definition of GMP

General Message Passing (GMP) means: a smart contract on a source chain can emit an arbitrary data payload that is reliably delivered to a destination chain and executed by another contract there.

The difference from a “token bridge” is foundational. A token bridge handles assets (lock on one side, mint a representation on the other). GMP handles arbitrary data. Token bridges are one specialization of GMP, but GMP is far more general — you can ship a swap instruction, a governance vote, an NFT metadata update, even a bytecode call into another contract.

Every modern cross-chain protocol (LayerZero, Wormhole, Axelar, Chainlink CCIP, Hyperlane) is a GMP protocol. Bridges are applications built on top of GMP, not the other way around.

A four-step framework

I break GMP protocols into four stages. The framework applies to all major protocols; only the implementation of each step differs:

  1. Source-chain event fires — the application contract emits a message event
  2. Off-chain relayer carries it — one or more off-chain nodes move the message to the destination
  3. Validator set signs / proves it — a group of validators attests that the message is real and untampered
  4. Destination contract executes — the destination endpoint calls the receiver contract’s specified method

The protocols differ mainly in step 3: who verifies, how they verify, and what the consequences are when they verify wrong.

Step one: what happens on the source chain

Suppose you have an Ethereum mainnet contract that wants to send a message to a contract on Arbitrum. Your code looks roughly like:

function sendCrossChain(uint256 dstChainId, address dstAddress, bytes memory payload) external payable {
    Endpoint.send{value: msg.value}(
        dstChainId,
        abi.encodePacked(dstAddress),
        payload,
        payable(msg.sender),
        address(0x0),
        bytes("")
    );
}

Endpoint is the protocol-provided contract. When you call Endpoint.send, several things happen:

  • Endpoint computes the message nonce (replay protection) and the current application config
  • Endpoint emits a PacketSent(nonce, payload, ...) event
  • Endpoint implicitly signals the protocol’s DVN / Guardian / validator set to verify
  • You pay gas fee plus a protocol message fee

The key point: there is no “package and send” step. The message just becomes a log on Ethereum mainnet. Everything else is off-chain work.

Step two: what the relayer does

The relayer (Executor in LayerZero, Spy in Wormhole, Relayer in Axelar, a DON node in CCIP) does:

  1. Watches PacketSent events from every Endpoint on the source chain
  2. Extracts destination chain, address, payload, nonce
  3. Wraps the message in the destination’s expected format
  4. Calls the destination Endpoint to submit “verify and execute this”

Relayers are permissionless. Anyone can run one and submit any message. But for the destination to accept it, the relayer must submit a verification proof alongside it (step 3).

Protocol Relayer form Bonding
LayerZero V2 Executor + DVN split DVN may bond
Wormhole Public Spy nodes None
Axelar Permissionless Relayer None
Chainlink CCIP Executing DON nodes DON bonds LINK
Hyperlane Modular relayer Defined by the ISM

Revenue comes from the destination execution gas you prepay on the source. The relayer uses it to pay destination gas and call the contract, keeping the spread.

Step three: what the validator set does

This is the largest design split in GMP. Mainstream choices:

Protocol Verification Trust assumption
LayerZero V2 DVN + app-configurable Trust the DVNs you select
Wormhole 19-Guardian multisig Trust 13/19 do not collude
Axelar PoS validator signatures Trust the AXL staker majority
CCIP Committing DON + RMN dual verification Trust DON + RMN are not both compromised
Hyperlane ISM (pluggable security modules) App defined

LayerZero V2 is the most flexible: apps choose 1 to N DVNs. You can require Polyhedra ZK DVN + Google Cloud DVN + your own DVN all sign. This configurable security stack is V2’s headline.

Wormhole’s 19-Guardian threshold is 13-of-19. Since the 2022 incident there have been no further compromises; 2025 added a ZK beacon verifier as an extra layer.

CCIP is the only protocol with economic + behavioral + governance fuse in three layers. The cost is more centralization: RMN actions can be human-triggered.

Comparison grid of GMP validator architectures

Step four: destination execution

When the message arrives the endpoint contract:

  1. Verifies the proof (signature set, ZK proof, or validator signatures)
  2. Checks the nonce
  3. Calls the destination method, typically lzReceive, _handle, or executeMessage
  4. Passes the payload as argument

The receiver must implement the protocol’s interface. For LayerZero:

function _lzReceive(
    Origin calldata _origin,
    bytes32 _guid,
    bytes calldata _message,
    address _executor,
    bytes calldata _extraData
) internal override {
    require(_origin.sender == bytes32(uint256(uint160(trustedSender))), "untrusted sender");
    // decode _message and execute
}

The most important thing: the receiver must verify the sender is trusted. Otherwise anyone can forge a message claiming to be from any source chain. This is the most common GMP bug — forgetting to check _origin.sender.

Common misconceptions

  1. Cross-chain messages are synchronous. They are not. There is a delay of seconds to minutes; your source contract cannot read destination state right after send.
  2. Messages always get delivered. Not guaranteed. If destination gas exceeds your prepayment, no relayer accepts the job and the message sits in “sent but unexecuted.” You retry or top up gas.
  3. Message order is FIFO. LayerZero V1 was FIFO; V2 is not by default. If you need ordering, enforce nonce checks at the app layer.
  4. Messages naturally reply. GMP is one-way. “Call and wait” requires the destination to send a reply — two independent operations.

Once you internalize these, you see why architecture gets complex. A simple cross-chain swap becomes a four-step dance: source sends message → destination executes swap → destination replies → source confirms.

How to start building

LayerZero V2 has the best docs and largest community. Path:

  1. Bootstrap oapp-evm with the LayerZero CLI
  2. Get hello world running in the simulator
  3. Deploy to testnet
  4. Use LayerZeroScan to observe state
  5. Add safety checks (trusted sender, nonce, pause)
  6. Then consider mainnet

If your destination includes Cosmos, pick Axelar GMP; for institutions, CCIP. See Axelar vs Chainlink CCIP for tradeoffs.

If your bridge intuition is not built, read the cross-chain bridge guide and the LayerZero usage tutorial. The Wormhole vs LayerZero difference is in this comparison.

A direct word to developers

Cross-chain is not the same mental model as single-chain. The most common trap is not a bug; it is failing to think through that messages can fail, lag, arrive out of order, and be unrecoverable. The SDK hides this; your app has to handle it.

The protocol guarantees a message can arrive; the message arriving correctly is entirely on you. Hold that line and your first GMP app is already ahead of most projects.