OP Stack interop is in active development. Some features may be experimental.
Overview
This tutorial demonstrates how to implement cross-chain communication within the OP Stack ecosystem. You’ll build a complete message passing system that enables different chains to interact with each other using theL2ToL2CrossDomainMessenger contract.
About this tutorial
About this tutorial
Prerequisite technical knowledge
- Intermediate Solidity programming
- Basic TypeScript knowledge
- Understanding of smart contract development
- Familiarity with blockchain concepts
- How to deploy contracts across different chains
- How to implement cross-chain message passing
- How to handle sender verification across chains
- How to relay messages manually between chains
- Unix-like operating system (Linux, macOS, or WSL for Windows)
- Node.js version 16 or higher
- Git for version control
- Foundry: For smart contract development
- Supersim: For local blockchain simulation (optional)
- TypeScript: For offchain code (for relaying messages manually)
- Viem: For interactions with the chain from the offchain app
What You’ll Build
- A
Greetercontract that stores and updates a greeting - A
GreetingSendercontract that sends cross-chain messages to update the greeting - A TypeScript application to relay messages between chains
This tutorial provides step-by-step instructions for implementing cross-chain messaging.
For a conceptual overview,
see the Message Passing Explainer.
L2ToL2CrossDomainMessenger contract to pass messages between interoperable blockchains.
Setting up your development environment
1
Follow Install Supersim to set up:
- Foundry for smart contract development (required in all cases)
- Supersim for local blockchain simulation (optional)
2
Verify your installation:
Implementing onchain message passing (in Solidity)
The implementation consists of three main components:- Greeter Contract: Deployed on
Chain B, receives and stores messages. - GreetingSender Contract: Deployed on
Chain A, initiates cross-chain messages.
1
Setting up test networks
-
If you are using Supersim, go to the directory where Supersim is installed and start it with autorelay.
If you are using the devnets, just skip this step.
- Supersim
- Devnets
Supersim creates threeanvilblockchains: -
In a separate shell, store the configuration in environment variables.
- Supersim
- Devnets
Set these parameters for Supersim.
Sanity check
Sanity check
To verify that the chains are running, check the balance of
$USER_ADDRESS.2
Create the contracts
-
Create a new Foundry project.
-
In
src/Greeter.solput this file. This is a variation on Hardhat’s Greeter contract. -
Deploy the
Greetercontract to Chain B and store the resulting contract address in theGREETER_B_ADDRESSenvironment variable.Explanation
The command that deploys the contract is:The command output gives us the deployer address, the address of the new contract, and the transaction hash:Theawkcommand looks for the line that hasDeployed to:and writes the third word in that line, which is the address.Finally, in UNIX (including Linux and macOS) when the command line includes backticks, the shell executes the code between the backticks and puts the output, in this case the contract address, in the command. So we get.
Sanity check
Sanity check
Run these commands to verify the contract works.
The first and third commands retrieve the current greeting, while the second command updates it.
-
Install the Optimism Solidity libraries into the project.
-
Create
src/GreetingSender.sol.Explanation
This function encodes a call tosetGreetingand sends it to a contract on another chain.abi.encodeCall(Greeter.setGreeting, (greeting))constructs the calldata by encoding the function selector and parameters. The encoded message is then passed tomessenger.sendMessage, which forwards it to the destination contract (greeterAddress) on the specified chain (greeterChainId).This ensures thatsetGreetingis executed remotely with the providedgreetingvalue (as long as there is an executing message to relay it). -
Deploy
GreetingSenderto chain A.
3
Send a message
Send a greeting from chain A to chain B.The
sleep call is because it can take up to two seconds until the transaction is included in chain A, and then up to two seconds until the relay transaction is included in chain B.Sender information
Run this command to view the events to see who calledsetGreeting.
L2ToL2CrossDomainMessenger contract address (4200000000000000000000000000000000000023), making it ineffective for identifying the original sender.
In this section we change Greeter.sol to emit a separate event in it receives a cross domain message, with the sender’s identity (address and chain ID).
1
Modify the Greeter contract
-
Modify
src/Greeter.solto this code.Explanation
This definition isn’t part of the npmjs package at writing, so we just add it here.If we see that we got a message fromL2ToL2CrossDomainMessenger, we callL2ToL2CrossDomainMessenger.crossDomainMessageContext. -
Redeploy the contracts.
Because the address of
Greeteris immutable inGreetingSender, we need to redeploy both contracts.
2
Verify you can see cross chain sender information
-
Set the greeting through
GreetingSender. -
Read the log entries.
See that the second topic (the first indexed log parameter) is the same as
$GREETER_A_ADDRESS. The third topic can be either0x385=901, which is the chain ID for supersim chain A, or0x190a85c0=420120000, which is the chain ID for devnet alpha 0.
Next steps
- Review the OP Stack Interop Explainer for answers to common questions about interoperability.
- Read the Message Passing Explainer to understand what happens “under the hood”.
- Write a revolutionary app that uses multiple blockchains within the OP Stack ecosystem.