From e9b296b366c7eff16665950e872f515213d00e60 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 25 Apr 2025 07:51:06 -0500 Subject: [PATCH 1/6] WIP --- pages/interop/tutorials/message-passing.mdx | 182 +++++++++++--------- public/tutorials/GreetingSender.sol | 4 +- public/tutorials/app.mts | 6 +- public/tutorials/app_v2.mts | 34 ++-- 4 files changed, 122 insertions(+), 104 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index 5214c4597..e8c7ba2e8 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -22,8 +22,7 @@ categories: is_imported_content: 'false' --- -import { Callout } from 'nextra/components' -import { Steps } from 'nextra/components' +import { Callout, Steps, Tabs } from 'nextra/components' import { InteropCallout } from '@/components/WipCallout' import { AutorelayCallout } from '@/components/AutorelayCallout' @@ -132,23 +131,38 @@ For development purposes, we'll first use autorelay mode to handle message execu 2. In a separate shell, store the configuration in environment variables. - ```sh - USER_ADDR=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - PRIV_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - RPC_L1=http://localhost:8545 - RPC_A=http://localhost:9545 - RPC_B=http://localhost:9546 - ``` - + + + Set these parameters for Supersim. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + ``` + + + + For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. + + ```sh + USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + ``` + +
Sanity check - To verify that the chains are running, check the balance of `$USER_ADDR`. + To verify that the chains are running, check the balance of `$USER_ADDRESS`. ```sh - cast balance --ether $USER_ADDR --rpc-url $RPC_L1 - cast balance --ether $USER_ADDR --rpc-url $RPC_A - cast balance --ether $USER_ADDR --rpc-url $RPC_B + cast balance --ether $USER_ADDRESS --rpc-url $URL_CHAIN_A + cast balance --ether $USER_ADDRESS --rpc-url $URL_CHAIN_B ```
@@ -168,42 +182,42 @@ For development purposes, we'll first use autorelay mode to handle message execu ```solidity file=/public/tutorials/Greeter.sol#L1-L20 hash=b3c5550bcc2cc4272125388ef23a67e7 ``` - 3. Deploy the `Greeter` contract to Chain B and store the resulting contract address in the `GREETER_B_ADDR` environment variable. + 3. Deploy the `Greeter` contract to Chain B and store the resulting contract address in the `GREETER_B_ADDRESS` environment variable. ```sh - GREETER_B_ADDR=`forge create --rpc-url $RPC_B --private-key $PRIV_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` + GREETER_B_ADDRESS=`forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` ``` -
- Explanation +
+ Explanation - The command that deploys the contract is: + The command that deploys the contract is: - ```sh - forge create --rpc-url $RPC_B --private-key $PRIV_KEY Greeter --broadcast - ``` + ```sh + forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast + ``` - The command output gives us the deployer address, the address of the new contract, and the transaction hash: + The command output gives us the deployer address, the address of the new contract, and the transaction hash: - ``` - Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - Deployed to: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 - Transaction hash: 0xf155d360ec70ee10fe0e02d99c16fa5d6dc2a0e79b005fec6cbf7925ff547dbf - ``` + ``` + Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + Deployed to: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 + Transaction hash: 0xf155d360ec70ee10fe0e02d99c16fa5d6dc2a0e79b005fec6cbf7925ff547dbf + ``` - The [`awk`](https://www.tutorialspoint.com/awk/index.htm) command looks for the line that has `Deployed to:` and writes the third word in that line, which is the address. + The [`awk`](https://www.tutorialspoint.com/awk/index.htm) command looks for the line that has `Deployed to:` and writes the third word in that line, which is the address. - ```sh - awk '/Deployed to:/ {print $3}' - ``` + ```sh + awk '/Deployed to:/ {print $3}' + ``` - Finally, in UNIX (including Linux and macOS) the 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. + Finally, in UNIX (including Linux and macOS) the 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. - ```sh - GREETER_B_ADDR= - ``` -
+ ```sh + GREETER_B_ADDRESS= + ``` +
Sanity check @@ -212,9 +226,9 @@ For development purposes, we'll first use autorelay mode to handle message execu The first and third commands retrieve the current greeting, while the second command updates it. ```sh - cast call --rpc-url $RPC_B $GREETER_B_ADDR "greet()" | cast --to-ascii - cast send --private-key $PRIV_KEY --rpc-url $RPC_B $GREETER_B_ADDR "setGreeting(string)" Hello - cast call --rpc-url $RPC_B $GREETER_B_ADDR "greet()" | cast --to-ascii + cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii + cast send --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "setGreeting(string)" Hello$$ + cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii ```
@@ -238,7 +252,7 @@ For development purposes, we'll first use autorelay mode to handle message execu 6. Create `src/GreetingSender.sol`. - ```solidity file=/public/tutorials/GreetingSender.sol#L1-L28 hash=9ed77001810caf52bbaa94da8b0dc5c6 + ```solidity file=/public/tutorials/GreetingSender.sol hash=20b41e7ec5b54c7cb4f040a35a0f50f5 ```
@@ -257,7 +271,8 @@ For development purposes, we'll first use autorelay mode to handle message execu 7. Deploy `GreetingSender` to chain A. ```sh - GREETER_A_ADDR=`forge create --rpc-url $RPC_A --private-key $PRIV_KEY --broadcast GreetingSender --constructor-args $GREETER_B_ADDR 902 | awk '/Deployed to:/ {print $3}'` + CHAIN_ID_B=`cast chain-id --rpc-url $URL_CHAIN_B` + GREETER_A_ADDRESS=`forge create --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --broadcast GreetingSender --constructor-args $GREETER_B_ADDRESS $CHAIN_ID_B | awk '/Deployed to:/ {print $3}'` ``` ### Send a message @@ -265,10 +280,10 @@ For development purposes, we'll first use autorelay mode to handle message execu Send a greeting from chain A to chain B. ```sh - cast call --rpc-url $RPC_B $GREETER_B_ADDR "greet()" | cast --to-ascii - cast send --private-key $PRIV_KEY --rpc-url $RPC_A $GREETER_A_ADDR "setGreeting(string)" "Hello from chain A" + cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii + cast send --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A $GREETER_A_ADDRESS "setGreeting(string)" "Hello from chain A" sleep 2 - cast call --rpc-url $RPC_B $GREETER_B_ADDR "greet()" | cast --to-ascii + cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii ``` The `sleep` call is because the message is not relayed until the next chain B block, which can take up to two seconds. @@ -279,7 +294,7 @@ For development purposes, we'll first use autorelay mode to handle message execu Run this command to view the events to see who called `setGreeting`. ```sh -cast logs --rpc-url $RPC_B 'SetGreeting(address,string)' +cast logs --rpc-url $URL_CHAIN_B 'SetGreeting(address,string)' ``` The sender information is stored in the second event topic. @@ -334,26 +349,26 @@ In this section we change `Greeter.sol` to emit a separate event in it receives } ``` -
- Explanation +
+ Explanation - ```solidity - if (msg.sender == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) { - (address sender, uint256 chainId) = - messenger.crossDomainMessageContext(); - emit CrossDomainSetGreeting(sender, chainId, _greeting); - } - ``` + ```solidity + if (msg.sender == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) { + (address sender, uint256 chainId) = + messenger.crossDomainMessageContext(); + emit CrossDomainSetGreeting(sender, chainId, _greeting); + } + ``` - If we see that we got a message from `L2ToL2CrossDomainMessenger`, we call [`L2ToL2CrossDomainMessenger.crossDomainMessageContext`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol#L118-L126). -
+ If we see that we got a message from `L2ToL2CrossDomainMessenger`, we call [`L2ToL2CrossDomainMessenger.crossDomainMessageContext`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol#L118-L126). +
2. Redeploy the contracts. Because the address of `Greeter` is immutable in `GreetingSender`, we need to redeploy both contracts. ```sh - GREETER_B_ADDR=`forge create --rpc-url $RPC_B --private-key $PRIV_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` - GREETER_A_ADDR=`forge create --rpc-url $RPC_A --private-key $PRIV_KEY --broadcast GreetingSender --constructor-args $GREETER_B_ADDR 902 | awk '/Deployed to:/ {print $3}'` + GREETER_B_ADDRESS=`forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` + GREETER_A_ADDRESS=`forge create --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --broadcast GreetingSender --constructor-args $GREETER_B_ADDRESS $CHAIN_ID_B | awk '/Deployed to:/ {print $3}'` ``` ### Verify you can see cross chain sender information @@ -361,22 +376,23 @@ In this section we change `Greeter.sol` to emit a separate event in it receives 1. Set the greeting through `GreetingSender`. ```sh - cast call --rpc-url $RPC_B $GREETER_B_ADDR "greet()" | cast --to-ascii - cast send --private-key $PRIV_KEY --rpc-url $RPC_A $GREETER_A_ADDR "setGreeting(string)" "Hello from chain A, with a CrossDomainSetGreeting event" + cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii + cast send --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A $GREETER_A_ADDRESS "setGreeting(string)" "Hello from chain A, with a CrossDomainSetGreeting event" sleep 2 - cast call --rpc-url $RPC_B $GREETER_B_ADDR "greet()" | cast --to-ascii + cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii ``` 2. Read the log entries. ```sh - cast logs --rpc-url $RPC_B 'CrossDomainSetGreeting(address,uint256,string)' - echo $GREETER_A_ADDR + cast logs --rpc-url $URL_CHAIN_B 'CrossDomainSetGreeting(address,uint256,string)' + echo $GREETER_A_ADDRESS echo 0x385 | cast --to-dec + echo 0x190a85c0 | cast --to-dec ``` - See that the second topic (the first indexed log parameter) is the same as `$GREETER_A_ADDR`. - The third topic is `0x385=901`, which is the chain ID for chain A. + See that the second topic (the first indexed log parameter) is the same as `$GREETER_A_ADDRESS`. + The third topic can be either `0x385=901`, which is the chain ID for supersim chain A, or `0x190a85c0=420120000`, which is the chain ID for devnet alpha 0. ## Implement manual message relaying @@ -429,7 +445,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives We need them in the Node process that the shell creates. ```sh - export GREETER_A_ADDR GREETER_B_ADDR PRIV_KEY + export GREETER_A_ADDRESS GREETER_B_ADDRESS PRIVATE_KEY ```
@@ -438,8 +454,8 @@ In this section we change `Greeter.sol` to emit a separate event in it receives 1. Create a simple `src/app.mts` file. ```typescript - console.log(`Greeter A ${process.env.GREETER_A_ADDR}`) - console.log(`Greeter B ${process.env.GREETER_B_ADDR}`) + console.log(`Greeter A ${process.env.GREETER_A_ADDRESS}`) + console.log(`Greeter B ${process.env.GREETER_B_ADDRESS}`) ``` 2. Run the program. @@ -451,7 +467,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives ### Send a greeting - 1. Link the compiled versions of the onchain code, which include the ABI, to the source code. + 1. Link the compiled versions of the onchain code, which include the ABI, to the offchain code repository. ```sh cd src @@ -462,7 +478,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives 2. Create or replace `src/app.mts` with this code. - ```typescript file=/public/tutorials/app.mts#L1-L51 hash=8f6f776884b8e37ae613f7aea8cd6a3b + ```typescript file=/public/tutorials/app.mts hash=caec4e94c5ece6ff3841f2f5f3d17aae ``` 3. Run the program, see that a greeting from chain A is relayed to chain B. @@ -482,12 +498,12 @@ In this section we change `Greeter.sol` to emit a separate event in it receives ``` 2. In the window you used for your earlier tests, redeploy the contracts. - Export the addresses so we'll have them in JavaScript + Export the addresses so we'll have them in the offchain. ```sh cd ../onchain-code - export GREETER_B_ADDR=`forge create --rpc-url $RPC_B --private-key $PRIV_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` - export GREETER_A_ADDR=`forge create --rpc-url $RPC_A --private-key $PRIV_KEY --broadcast GreetingSender --constructor-args $GREETER_B_ADDR 902 | awk '/Deployed to:/ {print $3}'` + export GREETER_B_ADDRESS=`forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` + export GREETER_A_ADDRESS=`forge create --rpc-url $URL_CHAIN_A --private-key $PRIVATE_KEY --broadcast GreetingSender --constructor-args $GREETER_B_ADDRESS $CHAIN_ID_B | awk '/Deployed to:/ {print $3}'` cd ../offchain-code ``` @@ -511,35 +527,37 @@ In this section we change `Greeter.sol` to emit a separate event in it receives 1. Replace `src/app.mts` with: - ```typescript file=/public/tutorials/app_v2.mts hash=f0aef31ef2ce29590a37f18ca07e52a9 + ```typescript file=/public/tutorials/app_v2.mts hash=5b31a6d32c1a033531a90c8a18807ad7 ```
Explanation - ```typescript file=/public/tutorials/app_v2.mts#L11-L15 hash=84fb4799b2fdd18785f691d602567145 + ```typescript file=/public/tutorials/app_v2.mts#L9-L10 hash=2d062eb374989a8a40199a4d7dc8be6e ``` Import from the [`@eth-optimism/viem`](https://www.npmjs.com/package/@eth-optimism/viem) package. - ```typescript file=/public/tutorials/app_v2.mts#L22-L28 hash=28c42407a1a01774f25ca78535d93c6e + ```typescript file=/public/tutorials/app_v2.mts#L17-L31 hash=cf5ce47bcbcd80327230a6da689688f8 ``` - In addition to extending the wallets with [Viem public actions](https://viem.sh/docs/accounts/local#5-optional-extend-with-public-actions), extend with the OP-Stack actions, both the public ones and the ones that require an account. + In addition to extending the wallets with [Viem public actions](https://viem.sh/docs/accounts/local#5-optional-extend-with-public-actions), extend with the OP-Stack actions. + On wallet A we need the public actions, those that only read information. + On wallet B we need the wallet actions, the ones that require an account. - ```typescript file=/public/tutorials/app_v2.mts#L59 hash=fc1aae397872717a3ed364930cdab9fc + ```typescript file=/public/tutorials/app_v2.mts#L54 hash=23aa6f24baeb5757130361f30c1b0e9c ``` To relay a message we need the information in the receipt. Also, we need to wait until the transaction with the relayed message is actually part of a block. - ```typescript file=/public/tutorials/app_v2.mts#L61-L63 hash=d45ebbc0d53dfbecbd8d357eb83e1b68 + ```typescript file=/public/tutorials/app_v2.mts#L56-L59 hash=8cc99e67ee36474c81183108531cb295 ``` A single transaction can send multiple messages. But here we know we sent just one, so we look for the first one in the list. - ```typescript file=/public/tutorials/app_v2.mts#L64-L70 hash=079ffd3e6fdf4eff3653ee7a74078183 + ```typescript file=/public/tutorials/app_v2.mts#L60-L67 hash=b7ed7d70ba5ec84322beee5369c5bee5 ``` Here we first send the relay message on chain B, and then wait for the receipt for it. diff --git a/public/tutorials/GreetingSender.sol b/public/tutorials/GreetingSender.sol index f44dd9f3e..112dbbc4e 100644 --- a/public/tutorials/GreetingSender.sol +++ b/public/tutorials/GreetingSender.sol @@ -1,8 +1,8 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Predeploys } from "lib/optimism/packages/contracts-bedrock/src/libraries/Predeploys.sol"; -import { IL2ToL2CrossDomainMessenger } from "lib/optimism/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol"; +import { Predeploys } from "@eth-optimism/contracts-bedrock/src/libraries/Predeploys.sol"; +import { IL2ToL2CrossDomainMessenger } from "@eth-optimism/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol"; import { Greeter } from "src/Greeter.sol"; diff --git a/public/tutorials/app.mts b/public/tutorials/app.mts index b850cddc6..422d7246f 100644 --- a/public/tutorials/app.mts +++ b/public/tutorials/app.mts @@ -13,7 +13,7 @@ import { supersimL2A, supersimL2B } from '@eth-optimism/viem/chains' import greeterData from './Greeter.json' import greetingSenderData from './GreetingSender.json' -const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`) +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) const walletA = createWalletClient({ chain: supersimL2A, @@ -28,13 +28,13 @@ const walletB = createWalletClient({ }).extend(publicActions) const greeter = getContract({ - address: process.env.GREETER_B_ADDR as Address, + address: process.env.GREETER_B_ADDRESS as Address, abi: greeterData.abi, client: walletB }) const greetingSender = getContract({ - address: process.env.GREETER_A_ADDR as Address, + address: process.env.GREETER_A_ADDRESS as Address, abi: greetingSenderData.abi, client: walletA }) diff --git a/public/tutorials/app_v2.mts b/public/tutorials/app_v2.mts index e3ddbf39c..0858019eb 100644 --- a/public/tutorials/app_v2.mts +++ b/public/tutorials/app_v2.mts @@ -6,50 +6,49 @@ import { Address, } from 'viem' import { privateKeyToAccount } from 'viem/accounts' -import { supersimL2A, supersimL2B } from '@eth-optimism/viem/chains' - +import { supersimL2A, supersimL2B } from '@eth-optimism/viem/chains' import { walletActionsL2, publicActionsL2 } from '@eth-optimism/viem' - + import greeterData from './Greeter.json' import greetingSenderData from './GreetingSender.json' - -const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`) - + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + const walletA = createWalletClient({ chain: supersimL2A, transport: http(), account }).extend(publicActions) .extend(publicActionsL2()) - .extend(walletActionsL2()) +// .extend(walletActionsL2()) const walletB = createWalletClient({ chain: supersimL2B, transport: http(), account }).extend(publicActions) - .extend(publicActionsL2()) +// .extend(publicActionsL2()) .extend(walletActionsL2()) - + const greeter = getContract({ - address: process.env.GREETER_B_ADDR as Address, + address: process.env.GREETER_B_ADDRESS as Address, abi: greeterData.abi, client: walletB }) - + const greetingSender = getContract({ - address: process.env.GREETER_A_ADDR as Address, + address: process.env.GREETER_A_ADDRESS as Address, abi: greetingSenderData.abi, client: walletA }) - + const txnBHash = await greeter.write.setGreeting( ["Greeting directly to chain B"]) await walletB.waitForTransactionReceipt({hash: txnBHash}) - + const greeting1 = await greeter.read.greet() console.log(`Chain B Greeting: ${greeting1}`) - + const txnAHash = await greetingSender.write.setGreeting( ["Greeting through chain A"]) const receiptA = await walletA.waitForTransactionReceipt({hash: txnAHash}) @@ -62,10 +61,11 @@ const relayMessageParams = await walletA.interop.buildExecutingMessage({ log: sentMessage.log, }) const relayMsgTxnHash = await walletB.interop.relayCrossDomainMessage(relayMessageParams) - + const receiptRelay = await walletB.waitForTransactionReceipt({ hash: relayMsgTxnHash, }) - + const greeting2 = await greeter.read.greet() console.log(`Chain A Greeting: ${greeting2}`) + From d5e9c7c49fcb7a3297a1132fe651dfe2aab36475 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 25 Apr 2025 07:51:37 -0500 Subject: [PATCH 2/6] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- words.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/words.txt b/words.txt index 8af644f62..186d6b4ac 100644 --- a/words.txt +++ b/words.txt @@ -95,8 +95,6 @@ Devnet devnet Devnets devnets -Devs - direnv DISABLETXPOOLGOSSIP disabletxpoolgossip From dcf96d0ac0bbca93503d3708cda6c5bcbed66b12 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 25 Apr 2025 14:37:17 -0500 Subject: [PATCH 3/6] Ready --- pages/interop/tutorials/message-passing.mdx | 169 ++++++++------------ public/tutorials/GreetingSender.sol | 4 +- 2 files changed, 72 insertions(+), 101 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index e8c7ba2e8..52ac976fa 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -35,66 +35,69 @@ import { AutorelayCallout } from '@/components/AutorelayCallout' This tutorial demonstrates how to implement cross-chain communication within the Superchain ecosystem. You'll build a complete message passing system that enables different chains to interact with each other using the `L2ToL2CrossDomainMessenger` contract. -### What You'll Build - -* A Greeter contract that stores and updates messages -* A GreetingSender contract that sends cross-chain messages -* A TypeScript application to relay messages between chains +
-### What you'll learn +About this tutorial -* 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 - - - This tutorial provides step-by-step instructions for implementing cross-chain messaging. - For a conceptual overview, - see the [message passing explainer](/interop/message-passing). - - -In this tutorial, you will learn how to use the [`L2ToL2CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol) contract to pass messages between interoperable blockchains. - -## Prerequisites - -Before starting this tutorial, ensure your development environment meets the following requirements: - -### Technical knowledge +**Prerequisite technical knowledge** * Intermediate Solidity programming * Basic TypeScript knowledge * Understanding of smart contract development * Familiarity with blockchain concepts -### Development environment + +**What you'll learn** + +* 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 + +**Development environment** * Unix-like operating system (Linux, macOS, or WSL for Windows) * Node.js version 16 or higher * Git for version control -### Required tools +**Required tools** The tutorial uses these primary tools: * Foundry: For smart contract development -* Supersim: For local blockchain simulation -* TypeScript: For implementation -* Viem: For blockchain interaction +* 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 `Greeter` contract that stores and updates a greeting +* A `GreetingSender` contract 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](/stack/interop/message-passing). + + +In this tutorial, you will learn how to use the [`L2ToL2CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol) contract to pass messages between interoperable blockchains. ## Setting up your development environment ### Follow the [Installation Guide](/app-developers/tutorials/supersim/getting-started/installation) to install: - * Foundry for smart contract development - * Supersim for local blockchain simulation + * Foundry for smart contract development (required in all cases) + * Supersim for local blockchain simulation (optional) ### Verify your installation: ```sh forge --version - supersim --version + ./supersim --version ``` @@ -104,30 +107,43 @@ The implementation consists of three main components: 1. **Greeter Contract**: Deployed on `Chain B`, receives and stores messages. 2. **GreetingSender Contract**: Deployed on `Chain A`, initiates cross-chain messages. -3. **Message relay system**: Ensures message delivery between chains. - -For development purposes, we'll first use autorelay mode to handle message execution automatically. Later sections cover [manual message relaying](#javascript-message-relaying) for production environments. ### Setting up test networks - - If you attempt to run these steps with the [devnet](/interop/tools/devnet), you *must* Send the executing message yourself, as explained [here](#implement-manual-message-relaying). - - - 1. In the directory where Supersim is installed, start it with autorelay. + 1. If you are using [Supersim](/interop/tools/supersim), go to the the directory where Supersim is installed and start it with autorelay. ```sh ./supersim --interop.autorelay ``` - Supersim creates three `anvil` blockchains: + If you are using [the devnets](/interop/tools/devnet), just skip this step. - | Role | ChainID | RPC URL | - | -------- | ------: | ---------------------------------------------- | - | L1 | 900 | [http://127.0.0.1:8545](http://127.0.0.1:8545) | - | OPChainA | 901 | [http://127.0.0.1:9545](http://127.0.0.1:9545) | - | OPChainB | 902 | [http://127.0.0.1:9546](http://127.0.0.1:9546) | + + + + Supersim creates three `anvil` blockchains: + + | Role | ChainID | RPC URL | + | -------- | ------: | ---------------------------------------------- | + | L1 | 900 | [http://127.0.0.1:8545](http://127.0.0.1:8545) | + | OPChainA | 901 | [http://127.0.0.1:9545](http://127.0.0.1:9545) | + | OPChainB | 902 | [http://127.0.0.1:9546](http://127.0.0.1:9546) | + + + + + + These are the three networks involved in the devnet: + + | Role | ChainID | RPC URL | + | ------------- | -------: | ---------------------------------------------- | + | L1 (Sepolia) | 11155111 | [https://eth-sepolia.public.blastapi.io](https://eth-sepolia.public.blastapi.io) | + | ChainA | 420120000 | [https://interop-alpha-0.optimism.io](https://interop-alpha-0.optimism.io) + | ChainB | 420120001 | [https://interop-alpha-1.optimism.io](https://interop-alpha-1.optimism.io) + + + 2. In a separate shell, store the configuration in environment variables. @@ -252,7 +268,7 @@ For development purposes, we'll first use autorelay mode to handle message execu 6. Create `src/GreetingSender.sol`. - ```solidity file=/public/tutorials/GreetingSender.sol hash=20b41e7ec5b54c7cb4f040a35a0f50f5 + ```solidity file=/public/tutorials/GreetingSender.sol hash=75d197d1e1da112421785c2160f6a55a ```
@@ -282,11 +298,12 @@ For development purposes, we'll first use autorelay mode to handle message execu ```sh cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii cast send --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A $GREETER_A_ADDRESS "setGreeting(string)" "Hello from chain A" - sleep 2 + sleep 4 cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii ``` - The `sleep` call is because the message is not relayed until the next chain B block, which can take up to two seconds. + 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 @@ -378,7 +395,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives ```sh cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii cast send --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A $GREETER_A_ADDRESS "setGreeting(string)" "Hello from chain A, with a CrossDomainSetGreeting event" - sleep 2 + sleep 4 cast call --rpc-url $URL_CHAIN_B $GREETER_B_ADDRESS "greet()" | cast --to-ascii ``` @@ -399,6 +416,9 @@ In this section we change `Greeter.sol` to emit a separate event in it receives +If you are using the devnets, skip this section. +You will not be able to see the effects anyway, because you cannot disable autoforwarding on them. + ### Set up @@ -569,55 +589,6 @@ In this section we change `Greeter.sol` to emit a separate event in it receives npm start ``` - ### Using devnet - - The same contracts are deployed on [the devnet](/interop/tools/devnet). - You can relay messages in exactly the same way you'd do it on Supersim. - - | Contract | Network | Address | - | ---------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `Greeter` | [Devnet 1](/interop/tools/devnet#interop-devnet-1) | [`0x1A183FCf61053B7dcd2322BbE766f7E1946d3718`](https://sid.testnet.routescan.io/address/0x1A183FCf61053B7dcd2322BbE766f7E1946d3718) | - | `GreetingSender` | [Devnet 0](/interop/tools/devnet#interop-devnet-1) | [`0x9De9f84a4EB3616B44CF1d68cD1A9098Df6cB25f`](https://sid.testnet.routescan.io/address/0x9De9f84a4EB3616B44CF1d68cD1A9098Df6cB25f/contract/420120000/readContract?chainid=420120000) | - - To modify the program to relay messages on devnet, follow these steps: - - 1. In `src/app.mts`, replace these lines to update the chains and contract addresses. - - | Line number | New content | - | ----------: | -------------------------------------------------------------------------- | - | 9 | `import { interopAlpha0, interopAlpha1 } from '@eth-optimism/viem/chains'` | - | 23 | ` chain: interopAlpha0,` | - | 31 | ` chain: interopAlpha1,` | - | 39 | ` address: "0x1A183FCf61053B7dcd2322BbE766f7E1946d3718",` | - | 45 | ` address: "0x9De9f84a4EB3616B44CF1d68cD1A9098Df6cB25f",` | - - 2. Set `PRIV_KEY` to the private key of an address that has [Sepolia ETH](https://cloud.google.com/application/web3/faucet/ethereum/sepolia). - - ```sh - export PRIV_KEY=0x - ``` - - 3. Send ETH to the two L2 blockchains. - - ```sh - cast send --rpc-url https://endpoints.omniatech.io/v1/eth/sepolia/public --private-key $PRIV_KEY --value 0.001ether 0x7385d89d38ab79984e7c84fab9ce5e6f4815468a - cast send --rpc-url https://endpoints.omniatech.io/v1/eth/sepolia/public --private-key $PRIV_KEY --value 0.001ether 0x55f5c4653dbcde7d1254f9c690a5d761b315500c - ``` - - Wait a few minutes until you can see the ETH [on your explorer](https://sid.testnet.routescan.io/). - - 4. Rerun the test. - - ```sh - npm start - ``` - - 5. You can see the transactions in a block explorer. - - * The first transaction, which sets the greeting directly, [on the `Greeter` contract on interop1](https://sid.testnet.routescan.io/address/0x1A183FCf61053B7dcd2322BbE766f7E1946d3718). - * The second transaction, the initiation message for the cross chain greeting change, [on the `GreetingSender` contract on interop0](https://sid.testnet.routescan.io/address/0x9De9f84a4EB3616B44CF1d68cD1A9098Df6cB25f). - * The third transaction, the executing message for the cross chain greeting change, [on the `Greeter` contract on interop1 as an internal transaction](https://sid.testnet.routescan.io/address/0x1A183FCf61053B7dcd2322BbE766f7E1946d3718/internalTx). - ### Debugging To see what messages were relayed by a specific transaction you can use this code: diff --git a/public/tutorials/GreetingSender.sol b/public/tutorials/GreetingSender.sol index 112dbbc4e..79257d4c3 100644 --- a/public/tutorials/GreetingSender.sol +++ b/public/tutorials/GreetingSender.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { Predeploys } from "@eth-optimism/contracts-bedrock/src/libraries/Predeploys.sol"; -import { IL2ToL2CrossDomainMessenger } from "@eth-optimism/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol"; +import { IL2ToL2CrossDomainMessenger } from "@eth-optimism/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol"; import { Greeter } from "src/Greeter.sol"; @@ -25,4 +25,4 @@ contract GreetingSender { ); messenger.sendMessage(greeterChainId, greeterAddress, message); } -} \ No newline at end of file +} From c62bbcaba3710e13dd9e53f35421e67d1284595a Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 25 Apr 2025 14:37:55 -0500 Subject: [PATCH 4/6] Auto-fix: Update breadcrumbs, spelling dictionary and other automated fixes --- pages/interop/tutorials/message-passing.mdx | 153 ++++++++++---------- 1 file changed, 73 insertions(+), 80 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index 52ac976fa..57f2f1ebf 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -36,39 +36,36 @@ This tutorial demonstrates how to implement cross-chain communication within the message passing system that enables different chains to interact with each other using the `L2ToL2CrossDomainMessenger` contract.
+ About this tutorial -About this tutorial + **Prerequisite technical knowledge** -**Prerequisite technical knowledge** + * Intermediate Solidity programming + * Basic TypeScript knowledge + * Understanding of smart contract development + * Familiarity with blockchain concepts -* Intermediate Solidity programming -* Basic TypeScript knowledge -* Understanding of smart contract development -* Familiarity with blockchain concepts + **What you'll learn** + * 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 -**What you'll learn** + **Development environment** -* 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 -**Development environment** + **Required tools** -* Unix-like operating system (Linux, macOS, or WSL for Windows) -* Node.js version 16 or higher -* Git for version control - -**Required tools** - -The tutorial uses these primary tools: - -* 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 + The tutorial uses these primary tools: + * 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 @@ -80,7 +77,7 @@ The tutorial uses these primary tools: This tutorial provides step-by-step instructions for implementing cross-chain messaging. For a conceptual overview, - see the [message passing explainer](/stack/interop/message-passing). + see the [message passing explainer](/interop/message-passing). In this tutorial, you will learn how to use the [`L2ToL2CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol) contract to pass messages between interoperable blockchains. @@ -119,9 +116,8 @@ The implementation consists of three main components: If you are using [the devnets](/interop/tools/devnet), just skip this step. - + - Supersim creates three `anvil` blockchains: | Role | ChainID | RPC URL | @@ -129,48 +125,46 @@ The implementation consists of three main components: | L1 | 900 | [http://127.0.0.1:8545](http://127.0.0.1:8545) | | OPChainA | 901 | [http://127.0.0.1:9545](http://127.0.0.1:9545) | | OPChainB | 902 | [http://127.0.0.1:9546](http://127.0.0.1:9546) | - - These are the three networks involved in the devnet: - | Role | ChainID | RPC URL | - | ------------- | -------: | ---------------------------------------------- | - | L1 (Sepolia) | 11155111 | [https://eth-sepolia.public.blastapi.io](https://eth-sepolia.public.blastapi.io) | - | ChainA | 420120000 | [https://interop-alpha-0.optimism.io](https://interop-alpha-0.optimism.io) - | ChainB | 420120001 | [https://interop-alpha-1.optimism.io](https://interop-alpha-1.optimism.io) - + | Role | ChainID | RPC URL | + | ------------ | --------: | -------------------------------------------------------------------------------- | + | L1 (Sepolia) | 11155111 | [https://eth-sepolia.public.blastapi.io](https://eth-sepolia.public.blastapi.io) | + | ChainA | 420120000 | [https://interop-alpha-0.optimism.io](https://interop-alpha-0.optimism.io) | + | ChainB | 420120001 | [https://interop-alpha-1.optimism.io](https://interop-alpha-1.optimism.io) | - + 2. In a separate shell, store the configuration in environment variables. - + - Set these parameters for Supersim. - - ```sh - PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - URL_CHAIN_A=http://127.0.0.1:9545 - URL_CHAIN_B=http://127.0.0.1:9546 - INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 - ``` + Set these parameters for Supersim. + + ```sh + PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + URL_CHAIN_A=http://127.0.0.1:9545 + URL_CHAIN_B=http://127.0.0.1:9546 + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + ``` - For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. - - ```sh - USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` - URL_CHAIN_A=https://interop-alpha-0.optimism.io - URL_CHAIN_B=https://interop-alpha-1.optimism.io - INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 - ``` + For Devnet, specify in `PRIVATE_KEY` the private key you used for the setup script and then these parameters. + + ```sh + USER_ADDRESS=`cast wallet address --private-key $PRIVATE_KEY` + URL_CHAIN_A=https://interop-alpha-0.optimism.io + URL_CHAIN_B=https://interop-alpha-1.optimism.io + INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 + ``` - + +
Sanity check @@ -204,36 +198,36 @@ The implementation consists of three main components: GREETER_B_ADDRESS=`forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` ``` -
- Explanation +
+ Explanation - The command that deploys the contract is: + The command that deploys the contract is: - ```sh - forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast - ``` + ```sh + forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast + ``` - The command output gives us the deployer address, the address of the new contract, and the transaction hash: + The command output gives us the deployer address, the address of the new contract, and the transaction hash: - ``` - Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - Deployed to: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 - Transaction hash: 0xf155d360ec70ee10fe0e02d99c16fa5d6dc2a0e79b005fec6cbf7925ff547dbf - ``` + ``` + Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + Deployed to: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 + Transaction hash: 0xf155d360ec70ee10fe0e02d99c16fa5d6dc2a0e79b005fec6cbf7925ff547dbf + ``` - The [`awk`](https://www.tutorialspoint.com/awk/index.htm) command looks for the line that has `Deployed to:` and writes the third word in that line, which is the address. + The [`awk`](https://www.tutorialspoint.com/awk/index.htm) command looks for the line that has `Deployed to:` and writes the third word in that line, which is the address. - ```sh - awk '/Deployed to:/ {print $3}' - ``` + ```sh + awk '/Deployed to:/ {print $3}' + ``` - Finally, in UNIX (including Linux and macOS) the 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. + Finally, in UNIX (including Linux and macOS) the 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. - ```sh - GREETER_B_ADDRESS= - ``` -
+ ```sh + GREETER_B_ADDRESS= + ``` +
Sanity check @@ -303,7 +297,6 @@ The implementation consists of three main components: ``` 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 @@ -366,7 +359,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives } ``` -
+
Explanation ```solidity @@ -378,7 +371,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives ``` If we see that we got a message from `L2ToL2CrossDomainMessenger`, we call [`L2ToL2CrossDomainMessenger.crossDomainMessageContext`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol#L118-L126). -
+
2. Redeploy the contracts. Because the address of `Greeter` is immutable in `GreetingSender`, we need to redeploy both contracts. From b97da8cc57569c0ced00af4254d6adf0bfe8227f Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 25 Apr 2025 14:43:18 -0500 Subject: [PATCH 5/6] lint --- pages/interop/tutorials/message-passing.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index 57f2f1ebf..fbaac188b 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -116,7 +116,7 @@ The implementation consists of three main components: If you are using [the devnets](/interop/tools/devnet), just skip this step. - + Supersim creates three `anvil` blockchains: @@ -136,11 +136,11 @@ The implementation consists of three main components: | ChainA | 420120000 | [https://interop-alpha-0.optimism.io](https://interop-alpha-0.optimism.io) | | ChainB | 420120001 | [https://interop-alpha-1.optimism.io](https://interop-alpha-1.optimism.io) | - + 2. In a separate shell, store the configuration in environment variables. - + Set these parameters for Supersim. @@ -163,7 +163,7 @@ The implementation consists of three main components: INTEROP_BRIDGE=0x4200000000000000000000000000000000000028 ``` - +
Sanity check @@ -198,7 +198,7 @@ The implementation consists of three main components: GREETER_B_ADDRESS=`forge create --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY Greeter --broadcast | awk '/Deployed to:/ {print $3}'` ``` -
+
Explanation The command that deploys the contract is: @@ -227,7 +227,7 @@ The implementation consists of three main components: ```sh GREETER_B_ADDRESS= ``` -
+
Sanity check @@ -359,7 +359,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives } ``` -
+
Explanation ```solidity @@ -371,7 +371,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives ``` If we see that we got a message from `L2ToL2CrossDomainMessenger`, we call [`L2ToL2CrossDomainMessenger.crossDomainMessageContext`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol#L118-L126). -
+
2. Redeploy the contracts. Because the address of `Greeter` is immutable in `GreetingSender`, we need to redeploy both contracts. From 24f133e73bcfd63edb3a51fcad3ec779cb0006a7 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Fri, 25 Apr 2025 15:02:56 -0500 Subject: [PATCH 6/6] Coderabbit --- pages/interop/tutorials/message-passing.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pages/interop/tutorials/message-passing.mdx b/pages/interop/tutorials/message-passing.mdx index fbaac188b..8d70fd00a 100644 --- a/pages/interop/tutorials/message-passing.mdx +++ b/pages/interop/tutorials/message-passing.mdx @@ -77,7 +77,7 @@ message passing system that enables different chains to interact with each other This tutorial provides step-by-step instructions for implementing cross-chain messaging. For a conceptual overview, - see the [message passing explainer](/interop/message-passing). + see the [Message Passing Explainer](/interop/message-passing). In this tutorial, you will learn how to use the [`L2ToL2CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol) contract to pass messages between interoperable blockchains. @@ -108,7 +108,7 @@ The implementation consists of three main components: ### Setting up test networks - 1. If you are using [Supersim](/interop/tools/supersim), go to the the directory where Supersim is installed and start it with autorelay. + 1. If you are using [Supersim](/interop/tools/supersim), go to the directory where Supersim is installed and start it with autorelay. ```sh ./supersim --interop.autorelay @@ -410,7 +410,7 @@ In this section we change `Greeter.sol` to emit a separate event in it receives If you are using the devnets, skip this section. -You will not be able to see the effects anyway, because you cannot disable autoforwarding on them. +You will not be able to see the effects anyway because you cannot disable autoforwarding on them. ### Set up