ERC-7786 Cross-chain messaging gateway

Final · Other · created 2024-10-14

A common interface that contracts on different blockchains use to exchange arbitrary data. It lets messages be sent and received in the same way no matter which bridge is used.

Why it is needed

There are many kinds of cross-chain messaging protocols (bridges) that pass messages between chains, and they differ in their degree of decentralization, their architecture, and the properties they guarantee. The problem is that almost every protocol uses a different interface and workflow. As a result, moving a contract built for one bridge to another bridge is practically impossible, and building general-purpose contracts that rely on cross-chain communication is also hard. A contract tied to a single bridge becomes exposed to risk if that bridge halts or is deprecated. This standard defines a common interface and flow for sending and receiving messages, so that existing protocols can be used in the same way through adapter contracts. Its goal is to make bridges easier to swap and combine, and to reduce vendor lock-in.

What to implement

In depth

The five components of a message

A cross-chain message consists of a sender, a recipient, a payload, a native token value, and an attribute list. The sender and recipient addresses must be entered in the binary format of the Interoperable Address from ERC-7930 and must be serialized according to CAIP-350. This makes it possible to represent addresses on non-EVM chains as well. The recipient field can be omitted or filled with all zeros, which is used when the protocol decides the final recipient, as in a broadcast. The payload is an opaque bytes value that is not interpreted.

A modular design extended with attributes

An attribute is a key-value pair. The key is 4 bytes (bytes4) and the value is a byte array (bytes). It is recommended to define the key as a Solidity ABI function selector. For example, the signature minGasLimit(uint256) becomes the key 39f87ba1, and the value encoding follows the ABI encoding of the function arguments, so you get a human-readable name and a standard encoding at the same time. The attribute list is encoded as bytes[], and each element is the key and value concatenated (with a selector-style key, it can be built with abi.encodeWithSignature). A message with no attributes at all must be treated as a valid message. The set of valid attributes is extensible, and it is recommended to standardize it as an ERC and publish it. This exposes bridge-specific features without a dedicated interface, so a contract can keep its message representation as it is and change only the gateway.

Behavior rules of sendMessage

The Source Gateway must implement IERC7786GatewaySource. sendMessage must revert with UnsupportedAttribute if an unsupported attribute is included, and may revert if an attribute value does not fit its key. It may receive native tokens along with the call, but it must revert if a value is included for a feature it does not support. How this value is represented on the destination chain is not defined by the standard. It may generate and return a unique nonzero send identifier (sendId) or return 0, and it must emit the MessageSent event. The sendId may differ from the receiveId passed to the receiving side, because the receiveId may be built from values that cannot be obtained in the execution environment, such as a transaction id or a log index.

Post-processing and design intent

Even after the sendMessage call, additional actions may be needed to actually send the message. A typical one is paying the gas fee to execute the message on the destination chain. The exact interface for such actions is outside the scope of this standard. If the additional parameters are accepted as attributes, the post-processing step is unnecessary, but if they are not provided as attributes, an additional call to the gateway is needed to send the message. Where possible, it is recommended to design the gateway so that anyone with a stake in the message delivery can step in at this stage. A malicious actor providing wrong parameters must not be able to prevent someone else from successfully relaying the message.

Reception procedure and receiveMessage verification

The Destination Gateway is a contract that verifies messages sent from another chain, and its interface and calling method are outside the scope of the standard. However, the protocol must guarantee that it delivers the sent message to the recipient through the IERC7786Recipient interface, and the recipient must implement this interface. The receiveId must be empty or unique to that message within that gateway. receiveMessage must return IERC7786Recipient.receiveMessage.selector (0x2432ef26), and the gateway must check that the return value is correct and revert if it is not. The recipient must verify that the caller is a known gateway of a protocol it trusts.

Properties the protocol must guarantee

The protocol underlying a gateway pair must guarantee several properties. Safety means a message is delivered only if it was sent on the origin chain, only after the sending transaction is finalized, and exactly once rather than more than once. If there are several messages with the same parameters, each must be delivered separately. Liveness means a sent message is eventually delivered if the chain is healthy and the fee is paid, and Timeliness means it is delivered within a documented time limit, which is a recommendation. These properties must not rely on trust in centralized actors. For example, safety is best guaranteed by trustless means such as light client proofs or proofs from an open, decentralized validator set, and relaying is best guaranteed in a decentralized or permissionless way.

Why use a dedicated receiveMessage function

The gateways of some protocols support making arbitrary direct calls to the recipient. In this approach, the recipient must detect on its own that the call comes from the gateway, and it finds the source chain and sender address through the gateway's lookup functions. However, this approach lets anyone trigger any call on any contract through the gateway, so it is dangerous if the gateway holds assets such as ERC-20 tokens. Having a dedicated receiveMessage function on the recipient side can protect the assets or permissions held by the gateway from this kind of attack. If direct call functionality is needed, it can be built as a wrapper on top of a gateway that implements this standard.

Things to watch out for

Original ERC · ← ERCs