Skip to content
This repository was archived by the owner on Apr 6, 2026. It is now read-only.
Merged
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
3 changes: 2 additions & 1 deletion pages/stack/interop/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"explainer": "Interop explainer",
"cross-chain-message": "Anatomy of cross-chain message",
"supersim": "Supersim Multichain Development Environment",
"superchain-erc20": "SuperchainERC20 token standard"
"superchain-erc20": "SuperchainERC20 token standard",
Comment thread
cpengilly marked this conversation as resolved.
Outdated
Comment thread
krofax marked this conversation as resolved.
Outdated
"message-passing": "General message passing with Interop"
}
261 changes: 261 additions & 0 deletions pages/stack/interop/message-passing.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
---
title: 'Message Passing with Interop'
lang: en-US
description: 'Learn how to implement cross-chain message passing using interop messaging.'
---

import { Steps } from 'nextra/components'

# Message passing with Interop

This guide explains how to implement cross-chain message passing using interop messaging.
You'll learn the core concepts and implementation details independent of specific tooling.

## Message passing concepts

Before diving into implementation, it's crucial to understand the core concepts of interop message passing:

### Message structure

A message is a broadcast payload emitted from an identified source. It consists of:

* **Message Payload**: Raw bytes representing a Log, created by concatenating topics and data
* **Unique Identifier**: A structure that points to a specific log emission
* **Execution Context**: Information about where and how the message should be processed

### Message payload construction

```typescript
// Message payload is constructed by concatenating:
// 1. All event topics (each 32 bytes)
// 2. The event data
messagePayload = eventTopics.concat(eventData)
```

### Message identifier components

```solidity
struct Identifier {
address origin; // Contract that emitted the log
uint256 blockNumber; // Block number of emission
uint256 logIndex; // Position in block's logs array
uint256 timestamp; // Emission timestamp
uint256 chainId; // Chain identifier where message originated
}
```
Comment thread
krofax marked this conversation as resolved.

## How It works

### Message lifecycle

The cross-chain messaging process follows a specific lifecycle:

1. **Message Creation**: Events on source chain become initiating messages
2. **Message Serialization**: Converting log data into a standardized format
3. **Identifier Creation**: Generating unique identifiers for message tracking
4. **Message Execution**: Processing messages on destination chain

### Message processing flow

1. **Source chain**:
* Event emission triggers message creation
* System generates unique identifier
* Message is serialized and prepared for transmission

2. **Cross-Chain transit**:
* Message payload is prepared for relay
* System validates message structure
* Cross-chain proof is generated

3. **Destination chain**:
* Message receipt and validation
* Execution of message content
* Verification of successful processing

## Implementation guide

<Steps>
### Prepare message sending
Comment thread
krofax marked this conversation as resolved.
Outdated

The [CrossChainMessenger](https://github.com/ethereum-optimism/optimism/blob/92ed64e171c6eb9c6a080c626640e8836f0653cc/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol) contract serves as the primary interface for cross-chain communication.
Comment thread
krofax marked this conversation as resolved.
Outdated
It initializes the connection to the L2 messenger contract and provides the base functionality for sending messages across chains.
Comment thread
krofax marked this conversation as resolved.
Outdated

Key components:

* L2\_MESSENGER: The address of the L2 cross-domain messenger contract
* sendCrossChainMessage: Function to initiate cross-chain message sending

```solidity
contract CrossChainMessenger {
address public immutable L2_MESSENGER = 0x4200000000000000000000000000000000000023;

function sendCrossChainMessage(
address _target, // Destination contract address
bytes memory _message, // Message content to be sent
uint32 _gasLimit // Gas limit for execution on destination
) external {
IL2CrossDomainMessenger(L2_MESSENGER).sendMessage(
_target,
_message,
_gasLimit
);
}
}
```
Comment thread
krofax marked this conversation as resolved.
Outdated
Comment thread
krofax marked this conversation as resolved.
Outdated

### Send the message

This function handles the actual transmission of your message to the destination chain. It manages:

* Contract interaction with the messenger
* Gas limit settings for the transaction
* Error handling during transmission
* Returns the transaction hash for tracking

```typescript
async function sendMessage(
messenger: address, // Address of the messenger contract
target: address, // Destination contract address
message: bytes, // Message content to be transmitted
gasLimit: number // Execution gas limit
): Promise<string> {
// Send the message with appropriate gas settings
const tx = await contract.sendMessage(
target,
message,
gasLimit,
{ gasLimit: 200000 } // Transaction gas limit for sending
);

return tx.hash;
}
```
Comment thread
krofax marked this conversation as resolved.
Outdated
Comment thread
krofax marked this conversation as resolved.
Outdated

Comment thread
krofax marked this conversation as resolved.
### Create message identifier

Creates a unique identifier for tracking your message across chains. This function:

* Retrieves transaction details
* Locates the specific message event
* Constructs the identifier structure with:
* Origin address
* Block information
* Chain details
* Timing data

```typescript
async function createMessageIdentifier(
txHash: string, // Transaction hash of the sent message
provider: Provider // Network provider for accessing chain data
): Promise<Identifier> {
// Get transaction and block information
const receipt = await provider.getTransactionReceipt(txHash);
const block = await provider.getBlock(receipt.blockNumber);

// Find the message sent event
const sentEvent = receipt.logs.find(log =>
log.address === L2_MESSENGER_ADDRESS &&
log.topics[0] === SENT_MESSAGE_EVENT_SIGNATURE
);

if (!sentEvent) throw new Error("Message event not found");

// Construct and return the identifier
return {
origin: L2_MESSENGER_ADDRESS,
blockNumber: receipt.blockNumber,
logIndex: sentEvent.logIndex,
timestamp: block.timestamp,
chainId: provider.network.chainId
};
}
```

### Construct message payload

Assembles the complete message payload for transmission. This function:

* Concatenates event topics and data
* Formats the payload for cross-chain transmission
* Ensures proper byte alignment
* Maintains data integrity

```typescript
function constructMessagePayload(
topics: string[], // Event topics to include
data: string // Additional message data
): string {
// Combine topics and data into a single payload
return ethers.utils.hexConcat([
...topics,
data
]);
}
```

### Relay the message

Executes the message on the destination chain. This function:

* Creates a messenger contract instance
* Sends the relay transaction
* Handles gas estimation
* Manages transaction confirmation
* Returns the transaction hash

```typescript
async function relayMessage(
identifier: Identifier, // Message identifier from source chain
payload: string, // Constructed message payload
provider: Provider // Network provider for destination chain
): Promise<string> {
// Initialize messenger contract
const messenger = new Contract(
L2_MESSENGER_ADDRESS,
L2_MESSENGER_ABI,
provider.getSigner()
);

// Send relay transaction with appropriate gas limit
const tx = await messenger.relayMessage(
identifier,
payload,
{ gasLimit: 500000 }
);

return tx.hash;
}
```

### Verify message receipt

Confirms that your message was successfully processed on the destination chain. This function:

* Retrieves the transaction receipt
* Checks for the relay event
* Verifies processing status
* Returns success/failure status

```typescript
async function verifyMessageRelayed(
txHash: string, // Transaction hash of the relay transaction
provider: Provider // Network provider for destination chain
): Promise<boolean> {
// Get transaction receipt
const receipt = await provider.getTransactionReceipt(txHash);

// Check for successful relay event
return receipt.logs.some(log =>
log.address === L2_MESSENGER_ADDRESS &&
log.topics[0] === RELAYED_MESSAGE_EVENT_SIGNATURE
);
}
```
</Steps>

## Next steps

* More questions? Read our guide on the anatomy of a [cross-chain message](cross-chain-message)
* Use [Supersim](supersim), a local dev environment that simulates Superchain interop for testing applications against a local version of the Superchain.
* Use [viem bindings/actions](https://supersim.pages.dev/guides/interop/relay-using-viem.html)the guide will show you how to use viem bindings/actions to fetch identifiers and relay messages.
Comment thread
krofax marked this conversation as resolved.
Outdated
* Read how to manually [relay interop messages](https://supersim.pages.dev/guides/interop/manually-relaying-interop-messages-cast) with cast and `L2ToL2CrossDomainMessenger`
1 change: 1 addition & 0 deletions words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ computependingblock
confs
corsdomain
counterfactually
Crosschain
crosschain
Crossmint
daserver
Expand Down