How Does Cross-Chain Message Passing Actually Work? A Walkthrough for Developers
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.

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:
- Source-chain event fires — the application contract emits a message event
- Off-chain relayer carries it — one or more off-chain nodes move the message to the destination
- Validator set signs / proves it — a group of validators attests that the message is real and untampered
- 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:
- Watches
PacketSentevents from every Endpoint on the source chain - Extracts destination chain, address, payload, nonce
- Wraps the message in the destination’s expected format
- 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.

Step four: destination execution
When the message arrives the endpoint contract:
- Verifies the proof (signature set, ZK proof, or validator signatures)
- Checks the nonce
- Calls the destination method, typically
lzReceive,_handle, orexecuteMessage - 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
- 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. - 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.
- Message order is FIFO. LayerZero V1 was FIFO; V2 is not by default. If you need ordering, enforce nonce checks at the app layer.
- 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:
- Bootstrap
oapp-evmwith the LayerZero CLI - Get hello world running in the simulator
- Deploy to testnet
- Use LayerZeroScan to observe state
- Add safety checks (trusted sender, nonce, pause)
- 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.