@FogoChain sets a new benchmark in L1, built from the ground up as a SVM chain with one goal: deliver the best on-chain trading experience. Its purpose-driven design empowers real-time financial interactions unmatched by general-purpose chains.
Ever wondered what messaging protocols look like on-chain? Let’s explore @wormhole , @LayerZero_Core , and @hyperlane to discover how to send & receive cross-chain messages and what is required to make it happen.
Wormhole
The simplest messaging protocol with 1 massive contract that has been assigned the task of everything, fittingly called `Implementation.sol`. It implements 3 key features:
- A list of the current approved Guardians along with a governance structure.
- An endpoint that allows someone to send messages.
- And lastly, the logic to verify messages.
To send a message with Wormhole, first call `messageFee()` to get the fee. Then call `publishMessage(…)` to send the message. On `publishMessage` you specify an optional nonce, your message, and a consistencyLevel. `publishMessage` emits a message log and then returns a unique sequence number to you. Notice that you don’t specify a chain. This is because Wormhole messages are broadcast. A Wormhole message can be verified everywhere. It is the caller’s job to encode some kind of restricting logic.
Once the message is considered final as by the included consistency level – which can be set to anything from 1 confirmation to final – your message will get signed and becomes available to pickup in the gossip network. It can then be verified by calling `parseAndVerifyVM` on the destination chain, the signatures are extracted and compared against the hash of the message.
Notice that this implementation has no relaying logic or on-chain storage usage. It is pure: sending a message is done by emitting an event on their contract, and verifying it is done by comparing the configured guardians against the signatures. The best part about this structure is that if you wanted, you can ignore everything but the `publishMessage` call. You can copy `parseAndVerifyVM` into your own contract, choose your own relayers, implement your own relayer scheme. You can even implement `parseAndVerifyVM` on a chain not supported by Wormhole!
Hyperlane
As I have previously written, Hyperlane supports a wide range of verification pathways. As a result, their complexity can range from fairly simple to very complex. However, they have done a stellar job at abstracting a lot of this logic. For simplicity, let’s focus on their core product: Multisig ISM. ISM means Interchain Security Modules and is what Hyperlane uses to describe security models. This allows them to essentially collect all the other messaging protocols, which I have previously described as a Zoo. Instead of 1 big contract, logic is delegated to smaller contracts:
- `Mailbox.sol` is the main interface contract and implement handlers to handle message execution regardless of the security model.
- Various hooks are used to manage fees & relaying.
- External ISMs are used to verify the authenticity of messages.
Assuming you want to use default settings, sending a message requires calling `quoteDispatch(...)` to get the fee and then `dispatch()`. On this call you specify: the destination chain, the destination recipient, and the message. Optionally, you can also specify a hook like InterchainGasPaymaster to pay for relaying. Hyperlane allows for a significantly increased customization at the cost of simplicity, though at no point does it get out of hand. Additionally, it is expected that a system which has built in support to pay for relaying is slightly more complex than one without.
Hyperlane uses signed checkpoints to verify messages, designed such that any later signing checkpoint also signs previous checkpoints. Once a checkpoint has been signed, all message inside it and all previous messages can be verified. The Hyperlane Mailbox calls verify(...) on the ISM contract.
Since message verification happens outside the mailbox with no on-chain commitments, you can in theory call `ism.verify` yourself to avoid interacting with the Mailbox or even implement the associated logic yourself. Additionally, the destination field is only enforced in 1 line of code so if you wanted, you could in theory move the `process` (and/or the multisig ISM `verify`) logic to your contract and get the ability to broadcast messages like Wormhole. (Though you need to keep track of the validators yourself).
Config for hyperlane messaging is stored on the recipient, so it doesn’t suffer from an problem where you need to set new configs for every single support chain as you expand to new chains, since you can programmatically predict various config settings.
After the message is deemed final by validators, they will sign it and make it available to relayers. Unlike Wormhole, Hyperlane doesn’t have the concept of finality levels. That is because the economic security module assumes that all signed checkpoints are final otherwise validators can get slashed. Let’s also address the elephant in the room – final. Both Wormhole and Hyperlane allows dApps to set the confidence of inclusion to final. Final by Hyperlane is defined as: When validators deem that their economic security is not at risk of a reorg, where for Wormhole it is slightly more fluffy. However, abstracting confirmations & time into a single “final” is helpful for applications, since they can now delegate the concept of finality to messaging protocols that have spent more time to think about it. Kudos to Hyperlane and Wormhole.
LayerZero
Sending and receiving messages through LayerZero acts is more complicated than both Wormhole and Hyperlane. Considering I am guaranteed to get something wrong, I will focus on the highlights. LayerZero contains 2 main components:
- `EndpointV2.sol` is responsible for message flow: quoting, sending, verifying, and executing.
- Ultralight Nodes (ULNs) of which there are 2 types: Receive and Send.
- Additionally, DVNs are expected to implement a contract to quote the cost to submit message and for them to be paid.
Before we can begin sending messages with LayerZero, we may want to configure out dApp. LayerZero has a wide set of available configs, significantly more than both Wormhole and Hyperlane combined. You can configure: block confirmations, security threshold, custom receive & send libraries, executor, max message size, and DVN set. When sending the fee currency – LZ token or gas – can be configured. For each chain you are deployed on, you need to set the config for that chain specifically. Luckily, LZ does allow an array of configurations in a single call. However, once you add a new chain to your existing 20 chains, you need to configure that new chain for your 20 other deployments.
To send a message, you need to call `quote(...)` to get the fee estimation and then call `send(...)` with the fee. For both Wormhole & Hyperlane this fee doesn’t change very often (ignoring relayer fees), however, for LayerZero you need to pay DVNs (either LZ or — god forbid if you are deployed to many chains — your configured DVNs) to submit message validations on the remote chain. They do this by calling `verify(...)` on the receive library on the remote chain. Each and every message send with LZ needs to have every single DVN call `verify`.
Once the configured DVNs have all called `verify` (after your configured number of confirmations) the `verifiable(...)` function will return `true` for the message and the committer can call `verify` on the Endpoint. This deletes the proof inside the receive library and stores it in the endpoint. Then `lzReceive(...)` can be called which deletes the proof from the endpoint, increments a lazy nonce (to allow previously verified messages with lower nonces to be executed), and calls the receiver.
Unlike both Hyperlane and Wormhole, LayerZero has no concept of finality. They have some default confirmation recommendations, but that is it. Once the configured number of confirmations have been reached, DVNs will submit it.
Indeed, I am actually ignoring some details of LayerZero here: DVNs configs, gas price feeds, message configuration, message limits, allow lists, and message nonces & order. All of this adds up. LayerZero have so much on-chain logic – paying DVNs, DVNs submitting proofs, moving verifications from the receive library into the endpoint, calling verify and then lzReceive, paying the executor, and along the way quoting every single entity – and it uses a lot of gas. Based on our testing, sending a LayerZero sometimes costs twice that of the same Wormhole message. Using LayerZero instead of Wormhole for a @CatalystAMM swap — in its entirety — 40% more expensive.
Everything I have praised either Wormhole or Hyperlane for: simplicity, no on-chain message commits, flexible message verification, placing configuration inside the sender or recipient, ability to set finality levels instead of just confirmations, lightweight protocol, few important contracts, and relayer flexibility LayerZero doesn't have. Additionally, LayerZero contracts by themselves are significantly more complex to understand since they are spread across projects. LayerZero is spaghetti.