Skip to content
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",
"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
}
```

## 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

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.
It initializes the connection to the L2 messenger contract and provides the base functionality for sending messages across chains.

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
);
}
}
```

### 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;
}
```

### 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.
* 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
Loading