Why
The product is not a bridge, and reading it as one hides the interesting part. LayerZero is a message layer: an application inherits OApp and sends an arbitrary payload from its contract on one chain to its contract on another, with OFT and ONFT as the token-shaped wrappers over that. The design choice underneath is that verification is modular — a Decentralized Verifier Network set that the application selects and composes, rather than a security assumption fixed by the protocol.
That modularity is genuinely better and it relocates a decision onto you, silently. A protocol with fixed security has one trust assumption and everyone inherits it, for good and ill. A protocol with selectable verification has as many assumptions as there are applications — and an application that never makes the choice does not escape it, it accepts a default. The failure is not that the default is bad. It is that the default is invisible: nothing in the repository says who verifies your messages, so the most security-relevant fact about the integration is absent from every code review of it.
Which is why the practical habit is worth more than the tutorial. Run the config query after the first deploy and paste the output where people read it. The question who am I trusting right now has an answer at every moment; the only variable is whether anyone has written it down. Compare third-party-blast-radius, which asks the same thing about dependencies, and storage-collision-admin-takeover, where the load-bearing fact also lived outside any single file.
The second trap is a silent one, and worth stating precisely. The sender pre-pays destination gas by encoding it in options. Under-provision it and the send looks successful while execution fails on the destination — a message that appears delivered and did nothing. Any production path over this needs a retry story written before it is needed, because the failure does not announce itself at the call site.
And the strategic note belongs to a different card. ATLAS moves LayerZero from infrastructure to market infrastructure, which changes who its competitors are — no longer Wormhole and Axelar but chains that own an exchange. headless-exchange-engines carries that argument. What belongs here is the engineering residue: the four-way split ATLAS publishes — matching, clearing, settlement, risk — is a usable module boundary, and separating risk from settlement is the part worth copying whether or not the product is ever used.
How it works
The first contact, and the three lines that matter
Endpoint address, destination EID, and options encoding — everything else is ordinary Solidity.
import { OApp, Origin, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";
import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";
contract PingPong is OApp {
using OptionsBuilder for bytes;
string public lastMessage;
constructor(address _endpoint, address _owner) OApp(_endpoint, _owner) {}
function send(uint32 dstEid, string calldata text) external payable {
bytes memory payload = abi.encode(text);
// destination gas is chosen by the sender — the UX core and the first trap
bytes memory options = OptionsBuilder.newOptions()
.addExecutorLzReceiveOption(80_000, 0);
_lzSend(dstEid, payload, options, MessagingFee(msg.value, 0), payable(msg.sender));
}
function _lzReceive(
Origin calldata, bytes32, bytes calldata payload, address, bytes calldata
) internal override {
lastMessage = abi.decode(payload, (string));
}
}
npx hardhat lz:oapp:config:get # read the defaults — this is the trust assumption
npx hardhat lz:oapp:send --dst-eid 40161 --text "hello"
The two traps, and why both are silent
| Trap | What it looks like | Why nothing catches it |
|---|---|---|
| Under-provisioned destination gas | Send succeeds, destination execution fails | The failure is on another chain; the call site returns fine |
| Untouched DVN configuration | Everything works | Nothing in the repo names the verifier, so review has nothing to object to |
Both are the same class: a decision made by omission, recorded nowhere. The habit that fixes both costs one command and one paragraph in a README.
Cost, and where it varies
| Component | Paid to | Note |
|---|---|---|
| Source-chain gas | The source chain | Ordinary |
| Destination execution gas | Pre-paid at send | Quoted at send time — a congested destination moves the quote a lot |
| DVN fees | The verifier set you chose | Varies with the set; a reason to know what it is |
| Protocol subscription | — | None |
What to take for a settlement design
Use the message layer, minimise the bridge. For a market whose collateral sits on several chains, the common first design is to make the collateral token omnichain — and the principle worth keeping is that settlement finality happens on exactly one chain, with everything else restricted to display and deposit paths. A message layer widens what you can reach; it does not widen where truth lives. And ATLAS's module boundary — risk separated from settlement — is worth copying independently of the product, because it is the split plumbing-skills-buyback argues most systems never make.