Skip to content

How do use cast to relay messages #1589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion pages/interop/reading-logs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ The process works through the [`CrossL2Inbox`](https://github.com/ethereum-optim

### Key components

* **[Identifier](/interop/tutorials/relay-messages-cast#message-identifier)**: A struct containing information about the log, including `chainId`, `origin` (contract address), and other log metadata
* **Identifier**: A struct containing information about the log, including `chainId`, `origin` (contract address), and other log metadata
* **[validateMessage](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol#L79)**: Function that verifies a log's authenticity before allowing its use

## Example: cross-chain attestation verification
Expand Down
4 changes: 0 additions & 4 deletions pages/interop/tutorials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ Documentation covering Interop related tutorials.

<Card title="Bridging native cross-chain ETH transfers" href="/interop/tutorials/bridge-crosschain-eth" icon={<img src="/img/icons/shapes.svg" />} />

<Card title="Relaying interop messages using `cast`" href="/interop/tutorials/relay-messages-cast" icon={<img src="/img/icons/shapes.svg" />} />

<Card title="Relaying interop messages using `viem`" href="/interop/tutorials/relay-messages-viem" icon={<img src="/img/icons/shapes.svg" />} />

<Card title="Making crosschain contract calls (ping pong)" href="/interop/tutorials/contract-calls" icon={<img src="/img/icons/shapes.svg" />} />

<Card title="Making crosschain event reads (tic-tac-toe)" href="/interop/tutorials/event-reads" icon={<img src="/img/icons/shapes.svg" />} />
Expand Down
4 changes: 2 additions & 2 deletions pages/interop/tutorials/_meta.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"message-passing": "Interop message passing",
"deploy-superchain-erc20": "Deploying a SuperchainERC20",
"deploy-superchain-erc20": "Deploying a SuperchainERC20",
"transfer-superchainERC20": "Transferring a SuperchainERC20",
"custom-superchain-erc20": "Custom SuperchainERC20 tokens",
"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`",
Expand Down
3 changes: 3 additions & 0 deletions pages/interop/tutorials/message-passing/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"relay-with-cast": "Manual relay transaction",
}
Comment on lines +2 to +3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid JSON (remove trailing comma)
The trailing comma after the single property makes this file unparsable. Remove it:

-     "relay-with-cast": "Manual relay transaction",
+     "relay-with-cast": "Manual relay transaction"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"relay-with-cast": "Manual relay transaction",
}
"relay-with-cast": "Manual relay transaction"
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 2-3: Expected a property but instead found '}'.

Expected a property here.

(parse)

220 changes: 220 additions & 0 deletions pages/interop/tutorials/message-passing/relay-with-cast.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
---
title: Manual relay transaction tutorial
description: >-
Learn to relay transactions directly by sending the correct transaction.
lang: en-US
content_type: tutorial
topic: interop-cast-manual-relay-tutorial
personas:
- protocol-developer
- chain-operator
- app-developer
categories:
- protocol
- interoperability
- cross-chain-messaging
- message-relaying
- cross-domain-messenger
- smart-contracts
- testnet
- superchain
is_imported_content: 'false'
---

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

# Manual relay transactions tutorial
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Align H1 with frontmatter title
The H1 reads “Manual relay transactions tutorial” but the frontmatter title is singular (“Manual relay transaction tutorial”). Please make them consistent.


<InteropCallout />

<AutorelayCallout />

## Overview

Learn to relay transactions directly by sending the correct transaction.

<details>
<summary>About this tutorial</summary>

**Prerequisite technical knowledge**

* Familiarity with blockchain concepts
* Familiarity with [Foundry](https://book.getfoundry.sh/getting-started/installation), especially `cast`

**What you'll learn**

* How to use `cast` to relay transactions with autorelay does not work

**Development environment**

* Unix-like operating system (Linux, macOS, or WSL for Windows)
* Node.js version 16 or higher
* Git for version control
* Supersim
</details>

### What You'll Build

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

## Setup

<Steps>
### Run Supersim

This exercise needs to be done on Supersim.
You cannot use the devnets because you cannot disable autorelay on them.

1. Follow the [Installation Guide](/app-developers/tutorials/supersim/getting-started/installation).

2. Run Supersim *without* `--interop.relay`.

```sh
./supersim
```

### Create the state for relaying messages

The results of this step are similar to what the [message passing tutorial](/interop/tutorials/message-passing) would produce if you did not have autorelay on.

Execute this script.

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

## Manually relay a message using `cast`

Run this script:

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

### Explanation

```sh
#! /bin/sh
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
USER_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
URL_CHAIN_A=http://localhost:9545
URL_CHAIN_B=http://localhost:9546
GREETER_A_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3
GREETER_B_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3
CHAIN_ID_B=902
```

This is the configuration.
The greeter addresses are the same because the nonce for the user address' nonce is the same.

```sh
cast send -q --private-key $PRIVATE_KEY --rpc-url $URL_CHAIN_A $GREETER_A_ADDRESS "setGreeting(string)" "Hello from chain A $$"
```

Send a message from chain A to chain B. The `$$` is the process ID, so if you rerun the script you'll see that the information changes.

```sh
cast logs "SentMessage(uint256,address,uint256,address,bytes)" --rpc-url $URL_CHAIN_A | tail -14 > log-entry
```

Whenever `L2ToL2CrossDomainMessenger` sends a message to a different blockchain, it emits [a `SendMessage` event](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol#L83-L91).
Here we look for those messages, but get only the last one.

<details>
<summary>Example `log-entry`</summary>

```yaml
- address: 0x4200000000000000000000000000000000000023
blockHash: 0xcd0be97ffb41694faf3a172ac612a23f224afc1bfecd7cb737a7a464cf5d133e
blockNumber: 426
data: 0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000064a41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001948656c6c6f2066726f6d20636861696e2041203131333030370000000000000000000000000000000000000000000000000000000000000000000000
logIndex: 0
removed: false
topics: [
0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320
0x0000000000000000000000000000000000000000000000000000000000000386
0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3
0x0000000000000000000000000000000000000000000000000000000000000000
]
transactionHash: 0x1d6f2e5e2c8f3eb055e95741380ca36492f784b9782848b66b66c65c5937ff3a
transactionIndex: 0
```
</details>

```sh
TOPICS=`cat log-entry | grep -A4 topics | awk '{print $1}' | tail -4 | sed 's/0x//'`
TOPICS=`echo $TOPICS | sed 's/ //g'`
```

Consolidate the log topics into a single hex string.

```sh
ORIGIN=0x4200000000000000000000000000000000000023
BLOCK_NUMBER=`cat log-entry | awk '/blockNumber/ {print $2}'`
LOG_INDEX=`cat log-entry | awk '/logIndex/ {print $2}'`
TIMESTAMP=`cast block $BLOCK_NUMBER --rpc-url $URL_CHAIN_A | awk '/timestamp/ {print $2}'`
CHAIN_ID_A=`cast chain-id --rpc-url $URL_CHAIN_A`
SENT_MESSAGE=`cat log-entry | awk '/data/ {print $2}'`
```

Read additional fields from the log entry.

```sh
LOG_ENTRY=0x`echo $TOPICS$SENT_MESSAGE | sed 's/0x//'`
```

Consolidate the entire log entry.

```sh
RPC_PARAMS=$(cat <<INNER_END_OF_FILE
{
"origin": "$ORIGIN",
"blockNumber": "$BLOCK_NUMBER",
"logIndex": "$LOG_INDEX",
"timestamp": "$TIMESTAMP",
"chainId": "$CHAIN_ID_A",
"payload": "$LOG_ENTRY"
}
INNER_END_OF_FILE
)

ACCESS_LIST=`cast rpc admin_getAccessListForIdentifier --rpc-url http://localhost:8420 "$RPC_PARAMS" | jq .accessList`
```

To solve some [denial of service issues](https://github.com/ethereum-optimism/design-docs/blob/main/protocol/interop-access-list.md), relay transactions have to have an access list with a checksum of the message.
This lets sequencers know what executing messages to expect in a transaction, which makes it easy not to include transactions that are invalid because they rely on messages that were never sent.

The [algorithm to calculate the access list](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol#L87-L115) is a bit complicated, but you don't need to worry about it.
Supersim exposes [RPC calls](https://supersim.pages.dev/guides/interop/cast?highlight=manuall#7-construct-the-access-list-for-the-message) that calculates it for you on port 8420.
Even if you use a different interop cluster that lost autorelay for some reason, the code above should calculate the correct access list because this is a [pure function](https://en.wikipedia.org/wiki/Pure_function). The other RPC call, `admin_getAccessListByMsgHash` is not and therefore is less flexible.

```sh
echo Old greeting
cast call $GREETER_B_ADDRESS "greet()(string)" --rpc-url $URL_CHAIN_B
```

Show the current greeting.
The message has not been relayed yet, so it's still the old greeting.

```sh
cast send -q $ORIGIN "relayMessage((address,uint256,uint256,uint256,uint256),bytes)" "($ORIGIN,$BLOCK_NUMBER,$LOG_INDEX,$TIMESTAMP,$CHAIN_ID_A)" $LOG_ENTRY --access-list "$ACCESS_LIST" --rpc-url $URL_CHAIN_B --private-key $PRIVATE_KEY
```

Call [`relayMessage`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol#L197-L256) to relay the message.

```sh
echo New greeting
cast call $GREETER_B_ADDRESS "greet()(string)" --rpc-url $URL_CHAIN_B
```

Again, show the current greeting.
Now it's the new one.

## Next steps

* Review the [Superchain Interop Explainer](/interop/explainer) for answers to common questions about interoperability.
* Read the [Message Passing Explainer](/interop/message-passing) to understand what happens "under the hood".
* Write a revolutionary app that uses multiple blockchains within the Superchain.
Loading