New ERC: Cross-chain broadcaster

New ERC: Crosschain Broadcaster

Abstract

This ERC defines a standardized protocol for cross-rollup message broadcasting and reception via storage proofs. Users can broadcast messages on a source chain, which can then be read by many other chains, as long as those chains share a common ancestor chain with the source chain. This smart contract standard enables trustless message passing across rollups hosted on different rollup architecture stacks.


Motivation

The Ethereum ecosystem is experiencing a rapid growth in the number of rollup chains. As the number of chains grows, the experience becomes more fragmented for users, creating a need for trustless “interop” between rollup chains. These rollup chains, hosted on different rollup stacks, have heterogenous properties, and as yet there does not exist a simple, trustless, unified mechanism for sending messages between these diverse chains.

Many classes of applications could benefit from a unified system for broadcasting messages across chains. Some examples include intent-based protocols, governance of multichain apps, multichain oracles and more.


Specification

Check out the full specification on Github: Add ERC: Crosschain Broadcaster by yahgwai · Pull Request #897 · ethereum/ERCs · GitHub

A work-in-progress reference implementation is also available here; if there is sufficient interest from the community, a full implementation can be built out: broadcast-erc/contracts/reference-impl at main · OffchainLabs/broadcast-erc · GitHub

Please participate in the discussion here to help us evaluate and iterate the Crosschain Broadcaster!


More Resources

Introducing the Crosschain Broadcaster - blog post with high level overview of the standard

FAQs for the Crosschain Broadcaster - common questions from the community thus far

7 Likes

Super interesting approach for cross-chain verification.

The trustless way of proving messages using storage proofs is what caught my attention. ( no centralized relayers, oracles, etc )

I have spent some time looking into the cross-chain-broadcaster contracts. While I believe currently it is at nascent stages, there are a couple of questions I have in mind.

Dropping them below:

  1. Regarding: SSTORE in broadcastMessage() function

Currently

  • the message and msg.sender together form the slot.
  • but, the slot is used to store the timestamp

The key assumption is: (msg.sender, message) → maps to a single storage slot. Once a message is written, any attempt to repeat the same message by the same sender will fail due to the require().

Questions

  1. My assumption as to why we store timestamp instead of the message itself is that it leads to each proof having a unique verifiable value, and this Helps off-chain provers quickly disambiguate proofs. And the stored message is time-bound attested which helps as well. Is this a correct understanding?

  2. Secondly, why does broadcast rely on the caller smart contracts to include a proper nonce in the message being passed to broadcastMessage() function?

There is a possibility that Solver X fulfills BOB’s intent for transferring 100 USDT. And later the same solver fulfils the same intent again.

In this case:

  • both fulfilment will lead to same message.
  • and the broadcast will reject to broadcast the 2nd message as it will see it as a repetition.
  • The broadcastMessage() function heavily relies on apps ( smart contract that uses CCB ) to implement nonces.

But, Why can’t Broadcaster not implement this on its own?

Possible routes:

  1. Add a nonce field per (msg.sender)

    → You broadcast (message, nonce) → stored at keccak256(message, nonce, msg.sender)

  2. Alternatively, include a timestamp or intent ID in the message

    → Ensures uniqueness even if semantic content is the same

  3. Standardize message construction (e.g., with domain separator, version, user, action, and nonce).


Overall love the idea.
Will drop a few more questions as I explore more.

1 Like

Hi zaryab, thanks for the thoughtful questions and feedback!

Originally, we had (msg.sender, message) -> bool indicating whether a message was sent. The Receiver would then check a storage proof that the slot for (msg.sender, message) is 0/1. We changed that to map to a timestamp simply because a bool was a waste of a slot, and many apps might want to know the timestamp of a message.

The reason we don’t have a nonce for messages is that it would slightly complicate the spec, use an extra storage slot, and not all apps would benefit from it. Apps can use whatever message semantics they want, and implement nonces if they need to be able to send the same message multiple times. The Burner example in the spec includes a nonce in its messages because the same user might burn multiple times, for example.

2 Likes