Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions pages/interop/tutorials/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
"transfer-superchainERC20": "Transferring a SuperchainERC20",
"custom-superchain-erc20": "Custom SuperchainERC20 tokens",
"bridge-crosschain-eth": "Bridging native cross-chain ETH transfers",
"relay-messages-cast": "Relaying interop messages using `cast`",
"relay-messages-viem": "Relaying interop messages using `viem`",
"contract-calls": "Making crosschain contract calls (ping pong)",
"event-reads": "Making crosschain event reads (tic-tac-toe)",
"event-contests": "Deploying crosschain event composability (contests)",
Expand Down
546 changes: 169 additions & 377 deletions pages/interop/tutorials/message-passing.mdx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pages/interop/tutorials/message-passing/_meta.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"relay-with-cast": "Relay transactions manually"
"manual-relay": "Relay transactions manually"
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ categories:
is_imported_content: 'false'
---

import { Callout } from 'nextra/components'
import { Steps } from 'nextra/components'
import { Callout, Steps } from 'nextra/components'
import { InteropCallout } from '@/components/WipCallout'
import { AutorelayCallout } from '@/components/AutorelayCallout'

Expand All @@ -47,6 +46,7 @@ Learn to relay transactions directly by sending the correct transaction.
**What you'll learn**

* How to use `cast` to relay transactions when autorelay does not work
* How to relay transactions using JavaScript

**Development environment requirements**

Expand All @@ -59,10 +59,13 @@ Learn to relay transactions directly by sending the correct transaction.

### What you'll build

* A script to relay messages without using [the JavaScript library](https://www.npmjs.com/package/@eth-optimism/viem)
* A program to relay messages without using [the JavaScript library](https://www.npmjs.com/package/@eth-optimism/viem)
* A shell script to relay messages using [`cast`](https://book.getfoundry.sh/cast/)

## Setup

These steps are necessary to run the tutorial, regardless of whether you are using `cast` or the JavaScript API.

<Steps>
### Run Supersim

Expand All @@ -83,19 +86,110 @@ Learn to relay transactions directly by sending the correct transaction.

Execute this script.

```sh file=<rootDir>/public/tutorials/setup-for-manual-relay.sh#L1-L147 hash=a63d72f58a06ca7ca78fd1592efcf4a3
```sh file=<rootDir>/public/tutorials/setup-for-manual-relay.sh hash=eea0a71ff6b4c27b91225a715eb2718e
```
</Steps>

## Manual relay using the API

<Steps>
### Setup

We are going to use a [Node](https://nodejs.org/en) project.

1. Initialize a new Node project.

```sh
mkdir -p manual-relay/offchain
cd manual-relay/offchain
npm init -y
npm install --save-dev -y viem @eth-optimism/viem
mkdir src
```

2. Export environment variables.
This is necessary because those variables are currently limited to the shell process.
We need them in the Node process that the shell creates.

```sh
export GREETER_A_ADDRESS GREETER_B_ADDRESS PRIVATE_KEY
```

### Manual relaying app

1. Create a file `manual-relay.mjs` with:

```javascript file=<rootDir>/public/tutorials/manual-relay.mjs hash=b0c44b327af300437f3d71ff049842f9
```

<details>
<summary>Explanation</summary>

```javascript file=<rootDir>/public/tutorials/manual-relay.mjs#L8-L9 hash=2d062eb374989a8a40199a4d7dc8be6e
```

Import from the [`@eth-optimism/viem`](https://www.npmjs.com/package/@eth-optimism/viem) package.

```javascript file=<rootDir>/public/tutorials/manual-relay.mjs#L18-L32 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.
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.

```javascript file=<rootDir>/public/tutorials/manual-relay.mjs#L55 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.

```javascript file=<rootDir>/public/tutorials/manual-relay.mjs#L57-L58 hash=c0d3d8a60143c30b93db256518e8b583
```

Show the user that until the relay transaction happens on chain B, the greeting is unchanged.

```javascript file=<rootDir>/public/tutorials/manual-relay.mjs#L60-L63 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.

```javascript file=<rootDir>/public/tutorials/manual-relay.mjs#L64-L71 hash=b7ed7d70ba5ec84322beee5369c5bee5
```

Here we first send the relay message on chain B, and then wait for the receipt for it.
</details>

2. Run JavaScript program, and see that the message is relayed.

```sh
node manual-relay.mjs
```

### Debugging

To see what messages were relayed by a specific transaction you can use this code:

```javascript
import { decodeRelayedL2ToL2Messages } from '@eth-optimism/viem'

const decodedRelays = decodeRelayedL2ToL2Messages(
{receipt: receiptRelay})

console.log(decodedRelays)
console.log(decodedRelays.successfulMessages[0].log)
```
</Steps>

## Manually relay a message using `cast`
## Manual relay using `cast`

Run this script:
Run this` script:

```sh
./manual-relay/sendAndRelay.sh
```

### Explanation
### What does the script do?

```sh
#! /bin/sh
Expand All @@ -109,7 +203,7 @@ CHAIN_ID_B=902
```

This is the configuration.
The greeter addresses are identical because the nonce for the user address has the same nonce on both chains.
The greeter addresses are identical because the nonce for the user address has an identical nonce on both chains.

```sh
cast send -q --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A $GREETER_A_ADDRESS "setGreeting(string)" "Hello from chain A $$"
Expand Down
8 changes: 4 additions & 4 deletions public/tutorials/GreetingSender.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//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/contracts-bedrock/src/L2/IL2ToL2CrossDomainMessenger.sol";

import { Greeter } from "src/Greeter.sol";

contract GreetingSender {
Expand All @@ -25,4 +25,4 @@ contract GreetingSender {
);
messenger.sendMessage(greeterChainId, greeterAddress, message);
}
}
}
51 changes: 0 additions & 51 deletions public/tutorials/app.mts

This file was deleted.

45 changes: 24 additions & 21 deletions public/tutorials/app_v2.mts → public/tutorials/manual-relay.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,60 @@ import {
http,
publicActions,
getContract,
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}`)

import { readFileSync } from 'fs';

const greeterData = JSON.parse(readFileSync('../onchain/out/Greeter.sol/Greeter.json'))
const greetingSenderData = JSON.parse(readFileSync('../onchain/out/Greeter.sol/Greeter.json'))

const account = privateKeyToAccount(process.env.PRIVATE_KEY)

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,
abi: greeterData.abi,
client: walletB
})

const greetingSender = getContract({
address: process.env.GREETER_A_ADDR as Address,
address: process.env.GREETER_A_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})

const greeting2 = await greeter.read.greet()
console.log(`Greeting before the relay transaction: ${greeting2}`)

const sentMessages = await walletA.interop.getCrossDomainMessages({
logs: receiptA.logs,
})
Expand All @@ -62,10 +65,10 @@ 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}`)
const greeting3 = await greeter.read.greet()
console.log(`Greeting after the relay transaction: ${greeting3}`)
Loading